xref: /core/svx/source/tbxctrls/tbxcolorupdate.cxx (revision 1062519e749ca983c488dd22e0035486c1841039)
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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sfx2/sfxbasemodel.hxx>
21 #include <sfx2/objsh.hxx>
22 #include <sfx2/namedcolor.hxx>
23 #include <svx/drawitem.hxx>
24 #include <tbxcolorupdate.hxx>
25 #include <svx/svxids.hrc>
26 #include <svx/xdef.hxx>
27 #include <svx/xlineit0.hxx>
28 #include <svx/xlndsit.hxx>
29 
30 #include <utility>
31 #include <vcl/commandinfoprovider.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/toolbox.hxx>
35 #include <vcl/virdev.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/settings.hxx>
38 
39 #include <svx/strings.hrc>
40 #include <svx/dialmgr.hxx>
41 #include <unotools/viewoptions.hxx>
42 #include <comphelper/string.hxx>
43 #include <o3tl/string_view.hxx>
44 
45 #include <frozen/unordered_map.h>
46 
47 namespace svx
48 {
ToolboxButtonColorUpdaterBase(bool bWideButton,OUString aCommandLabel,OUString aCommandURL,sal_uInt16 nSlotId,css::uno::Reference<css::frame::XFrame> xFrame)49 ToolboxButtonColorUpdaterBase::ToolboxButtonColorUpdaterBase(
50     bool bWideButton, OUString aCommandLabel, OUString aCommandURL, sal_uInt16 nSlotId,
51     css::uno::Reference<css::frame::XFrame> xFrame)
52     : mbWideButton(bWideButton)
53     , mbWasHiContrastMode(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
54     , mnSlotId(nSlotId)
55     , maCurColor(COL_TRANSPARENT)
56     , meImageType(vcl::ImageType::Size16)
57     , maCommandLabel(std::move(aCommandLabel))
58     , maCommandURL(std::move(aCommandURL))
59     , mxFrame(std::move(xFrame))
60 {
61 }
62 
Init(sal_uInt16 nSlotId)63 void ToolboxButtonColorUpdaterBase::Init(sal_uInt16 nSlotId)
64 {
65     if (mbWideButton)
66     {
67         Update(COL_TRANSPARENT, true);
68         return;
69     }
70 
71     if (rtl::Reference xModel
72         = dynamic_cast<SfxBaseModel*>(mxFrame->getController()->getModel().get()))
73     {
74         auto pDocSh = xModel->GetObjectShell();
75         StartListening(*pDocSh);
76         if (auto oColor = pDocSh->GetRecentColor(nSlotId))
77         {
78             Update(*oColor);
79             return;
80         }
81     }
82 
83     // tdf#72991 - remember last used color depending on slot id
84     const auto aSlotNamedColorMap = frozen::make_unordered_map<sal_uInt16, NamedColor>(
85         { { SID_ATTR_CHAR_COLOR,
86             NamedColor(COL_DEFAULT_FONT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FONT)) },
87           { SID_ATTR_CHAR_COLOR2,
88             NamedColor(COL_DEFAULT_FONT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FONT)) },
89           { SID_FRAME_LINECOLOR,
90             NamedColor(COL_DEFAULT_FRAMELINE, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FRAMELINE)) },
91           { SID_ATTR_CHAR_COLOR_BACKGROUND,
92             NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
93           { SID_ATTR_CHAR_BACK_COLOR,
94             NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
95           { SID_BACKGROUND_COLOR,
96             NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
97           { SID_TABLE_CELL_BACKGROUND_COLOR,
98             NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
99           { SID_ATTR_LINE_COLOR,
100             NamedColor(COL_DEFAULT_SHAPE_STROKE, SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_STROKE)) },
101           { SID_ATTR_FILL_COLOR, NamedColor(COL_DEFAULT_SHAPE_FILLING,
102                                             SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_FILLING)) }
103 
104         });
105 
106     const auto aIterator = aSlotNamedColorMap.find(nSlotId);
107     if (aIterator != aSlotNamedColorMap.end())
108     {
109         NamedColor aNamedColor(aIterator->second);
110         SvtViewOptions aViewOpt(EViewType::Dialog, u"ToolboxButtonColor"_ustr);
111         if (aViewOpt.Exists())
112         {
113             css::uno::Any aUserItem = aViewOpt.GetUserItem(OUString::number(nSlotId));
114             OUString aUserData;
115             if (aUserItem >>= aUserData)
116             {
117                 sal_Int32 nIdx = 0;
118                 aNamedColor.m_aName = o3tl::getToken(aUserData, 0, ';', nIdx);
119                 aNamedColor.m_aColor
120                     = Color(ColorTransparencyTag::ColorTransparency,
121                             o3tl::toUInt32(o3tl::getToken(aUserData, 0, ';', nIdx)));
122             }
123         }
124         SetRecentColor(aNamedColor, /*Broadcast=*/false);
125         Update(aNamedColor);
126     }
127     else
128         Update(COL_TRANSPARENT);
129 }
130 
Notify(SfxBroadcaster & rBC,const SfxHint & rHint)131 void ToolboxButtonColorUpdaterBase::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
132 {
133     if (rHint.GetId() == SfxHintId::Dying)
134     {
135         EndListeningAll();
136     }
137     else if (rHint.GetId() == SfxHintId::ColorsChanged)
138     {
139         if (auto oColor = static_cast<SfxObjectShell&>(rBC).GetRecentColor(mnSlotId))
140         {
141             Update(*oColor);
142             // tdf#72991 - remember last used color depending on slot id
143             const OUString aUserData
144                 = oColor->m_aName + ";"
145                   + OUString::number(static_cast<sal_uInt32>(oColor->m_aColor));
146             SvtViewOptions(EViewType::Dialog, u"ToolboxButtonColor"_ustr)
147                 .SetUserItem(OUString::number(mnSlotId), css::uno::Any(aUserData));
148         }
149     }
150 }
151 
SetRecentColor(const NamedColor & rNamedColor,bool bBroadcast)152 void ToolboxButtonColorUpdaterBase::SetRecentColor(const NamedColor& rNamedColor, bool bBroadcast)
153 {
154     if (rtl::Reference xModel
155         = dynamic_cast<SfxBaseModel*>(mxFrame->getController()->getModel().get()))
156         xModel->GetObjectShell()->SetRecentColor(mnSlotId, rNamedColor, bBroadcast);
157     else if (!mbWideButton)
158         Update(rNamedColor);
159 }
160 
VclToolboxButtonColorUpdater(sal_uInt16 nSlotId,ToolBoxItemId nTbxBtnId,ToolBox * pToolBox,bool bWideButton,const OUString & rCommandLabel,const OUString & rCommandURL,const css::uno::Reference<css::frame::XFrame> & rFrame)161 VclToolboxButtonColorUpdater::VclToolboxButtonColorUpdater(
162     sal_uInt16 nSlotId, ToolBoxItemId nTbxBtnId, ToolBox* pToolBox, bool bWideButton,
163     const OUString& rCommandLabel, const OUString& rCommandURL,
164     const css::uno::Reference<css::frame::XFrame>& rFrame)
165     : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, rCommandURL, nSlotId, rFrame)
166     , mnBtnId(nTbxBtnId)
167     , mpTbx(pToolBox)
168 {
169     Init(nSlotId);
170 }
171 
SetQuickHelpText(const OUString & rText)172 void VclToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
173 {
174     mpTbx->SetQuickHelpText(mnBtnId, rText);
175 }
176 
GetQuickHelpText() const177 OUString VclToolboxButtonColorUpdater::GetQuickHelpText() const
178 {
179     return mpTbx->GetQuickHelpText(mnBtnId);
180 }
181 
SetImage(VirtualDevice * pVirDev)182 void VclToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
183 {
184     GDIMetaFile* pMtf = pVirDev->GetConnectMetaFile();
185 
186     assert(pMtf && "should have been set in ToolboxButtonColorUpdaterBase::Update");
187 
188     pMtf->Stop();
189     pMtf->WindStart();
190 
191     Graphic aGraphic(*pMtf);
192 
193     mpTbx->SetItemImage(mnBtnId, Image(aGraphic.GetXGraphic()));
194 }
195 
CreateVirtualDevice() const196 VclPtr<VirtualDevice> VclToolboxButtonColorUpdater::CreateVirtualDevice() const
197 {
198     auto xVD = VclPtr<VirtualDevice>::Create(*mpTbx->GetOutDev());
199     xVD->SetBackground(Wallpaper(COL_WHITE));
200     return xVD;
201 }
202 
GetImageSize() const203 vcl::ImageType VclToolboxButtonColorUpdater::GetImageSize() const { return mpTbx->GetImageSize(); }
204 
GetItemSize(const Size & rImageSize) const205 Size VclToolboxButtonColorUpdater::GetItemSize(const Size& rImageSize) const
206 {
207     if (mbWideButton)
208         return mpTbx->GetItemContentSize(mnBtnId);
209     return rImageSize;
210 }
211 
~ToolboxButtonColorUpdaterBase()212 ToolboxButtonColorUpdaterBase::~ToolboxButtonColorUpdaterBase() {}
213 
Update(const NamedColor & rNamedColor)214 void ToolboxButtonColorUpdaterBase::Update(const NamedColor& rNamedColor)
215 {
216     Update(rNamedColor.m_aColor);
217 
218     // Also show the current color as QuickHelpText
219     OUString colorSuffix = u" (%1)"_ustr.replaceFirst("%1", rNamedColor.m_aName);
220     OUString colorHelpText = maCommandLabel + colorSuffix;
221     SetQuickHelpText(colorHelpText);
222 }
223 
Update(const Color & rColor,bool bForceUpdate)224 void ToolboxButtonColorUpdaterBase::Update(const Color& rColor, bool bForceUpdate)
225 {
226     vcl::ImageType eImageType = GetImageSize();
227 
228 #ifdef IOS // tdf#126966
229     eImageType = vcl::ImageType::Size32;
230 #endif
231 
232     const bool bSizeChanged = (meImageType != eImageType);
233     meImageType = eImageType;
234     const bool bDisplayModeChanged
235         = (mbWasHiContrastMode
236            != Application::GetSettings().GetStyleSettings().GetHighContrastMode());
237     Color aColor(rColor);
238 
239     // !!! #109290# Workaround for SetFillColor with COL_AUTO
240     if (aColor == COL_AUTO)
241         aColor = COL_TRANSPARENT;
242 
243     if ((maCurColor == aColor) && !bSizeChanged && !bDisplayModeChanged && !bForceUpdate)
244         return;
245 
246     const Image aImage
247         = vcl::CommandInfoProvider::GetImageForCommand(maCommandURL, mxFrame, meImageType);
248 
249     const Size aItemSize = GetItemSize(aImage.GetSizePixel());
250     if (!aItemSize.Width() || !aItemSize.Height())
251         return;
252 
253     ScopedVclPtr<VirtualDevice> pVirDev(CreateVirtualDevice());
254     pVirDev->SetOutputSizePixel(aItemSize, /*bErase*/ true, /*bAlphaMaskTransparent*/ true);
255 
256     std::unique_ptr<GDIMetaFile> xMetaFile;
257     if (RecordVirtualDevice())
258     {
259         xMetaFile.reset(new GDIMetaFile);
260         xMetaFile->SetPrefSize(pVirDev->GetOutputSize());
261         xMetaFile->SetPrefMapMode(pVirDev->GetMapMode());
262         xMetaFile->Record(pVirDev.get());
263         pVirDev->EnableOutput(false);
264     }
265 
266     tools::Rectangle aUpdateRect;
267     if (aItemSize.Width() == aItemSize.Height())
268         // tdf#84985 align color bar with icon bottom edge; integer arithmetic e.g. 26 - 26/4 <> 26 * 3/4
269         aUpdateRect = tools::Rectangle(Point(0, aItemSize.Height() - aItemSize.Height() / 4),
270                                        Size(aItemSize.Width(), aItemSize.Height() / 4));
271     else
272         aUpdateRect = tools::Rectangle(Point(aItemSize.Height() + 2, 2),
273                                        Point(aItemSize.Width() - 3, aItemSize.Height() - 3));
274 
275     pVirDev->Push(vcl::PushFlags::CLIPREGION);
276 
277     // tdf#135121 don't include the part of the image which we will
278     // overwrite with the target color so that for the transparent color
279     // case the original background of the device is shown
280     vcl::Region aRegion(tools::Rectangle(Point(0, 0), aItemSize));
281     aRegion.Exclude(aUpdateRect);
282     pVirDev->SetClipRegion(aRegion);
283 
284     pVirDev->DrawImage(Point(0, 0), aImage);
285 
286     pVirDev->Pop();
287 
288     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
289     mbWasHiContrastMode = rStyleSettings.GetHighContrastMode();
290 
291     if ((COL_TRANSPARENT != aColor) && (aItemSize.Width() == aItemSize.Height()))
292         pVirDev->SetLineColor(aColor);
293     else
294         pVirDev->SetLineColor(rStyleSettings.GetDisableColor());
295 
296     // use not only COL_TRANSPARENT for detection of transparence,
297     // but the method/way which is designed to do that
298     const bool bIsFullyTransparent(aColor.IsFullyTransparent());
299     maCurColor = aColor;
300 
301     if (bIsFullyTransparent)
302     {
303         pVirDev->SetFillColor();
304     }
305     else
306     {
307         pVirDev->SetFillColor(maCurColor);
308     }
309 
310     pVirDev->DrawRect(aUpdateRect);
311 
312     SetImage(pVirDev.get());
313 }
314 
GetCurrentColorName() const315 OUString ToolboxButtonColorUpdaterBase::GetCurrentColorName() const
316 {
317     OUString sColorName = GetQuickHelpText();
318     // The obtained string is of format: color context (color name)
319     // Generate a substring which contains only the color name
320     sal_Int32 nStart = sColorName.indexOf('(');
321     sColorName = sColorName.copy(nStart + 1);
322     sal_Int32 nLength = sColorName.getLength();
323     if (nLength > 0)
324         sColorName = sColorName.copy(0, nLength - 1);
325     return sColorName;
326 }
327 
ToolboxButtonColorUpdater(sal_uInt16 nSlotId,const OUString & rTbxBtnId,weld::Toolbar * ptrTbx,bool bWideButton,const OUString & rCommandLabel,const css::uno::Reference<css::frame::XFrame> & rFrame)328 ToolboxButtonColorUpdater::ToolboxButtonColorUpdater(
329     sal_uInt16 nSlotId, const OUString& rTbxBtnId, weld::Toolbar* ptrTbx, bool bWideButton,
330     const OUString& rCommandLabel, const css::uno::Reference<css::frame::XFrame>& rFrame)
331     : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, rTbxBtnId, nSlotId, rFrame)
332     , msBtnId(rTbxBtnId)
333     , mpTbx(ptrTbx)
334 {
335     Init(nSlotId);
336 }
337 
SetQuickHelpText(const OUString & rText)338 void ToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
339 {
340     mpTbx->set_item_tooltip_text(msBtnId, rText);
341 }
342 
GetQuickHelpText() const343 OUString ToolboxButtonColorUpdater::GetQuickHelpText() const
344 {
345     return mpTbx->get_item_tooltip_text(msBtnId);
346 }
347 
SetImage(VirtualDevice * pVirDev)348 void ToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
349 {
350     mpTbx->set_item_image(msBtnId, pVirDev);
351 }
352 
CreateVirtualDevice() const353 VclPtr<VirtualDevice> ToolboxButtonColorUpdater::CreateVirtualDevice() const
354 {
355     return mpTbx->create_virtual_device();
356 }
357 
GetImageSize() const358 vcl::ImageType ToolboxButtonColorUpdater::GetImageSize() const { return mpTbx->get_icon_size(); }
359 
GetItemSize(const Size & rImageSize) const360 Size ToolboxButtonColorUpdater::GetItemSize(const Size& rImageSize) const
361 {
362     auto nWidth = rImageSize.Width();
363     if (mbWideButton)
364         nWidth = nWidth * 5;
365     return Size(nWidth, rImageSize.Height());
366 }
367 
ToolboxButtonLineStyleUpdater()368 ToolboxButtonLineStyleUpdater::ToolboxButtonLineStyleUpdater()
369     : m_eXLS(css::drawing::LineStyle_NONE)
370     , m_nDashStyleIndex(-1)
371 {
372 }
373 
Update(const css::frame::FeatureStateEvent & rEvent)374 void ToolboxButtonLineStyleUpdater::Update(const css::frame::FeatureStateEvent& rEvent)
375 {
376     if (rEvent.FeatureURL.Complete == ".uno:LineDash")
377     {
378         m_nDashStyleIndex = -1;
379 
380         SfxObjectShell* pSh = SfxObjectShell::Current();
381         if (!pSh)
382             return;
383         const SvxDashListItem* pItem = pSh->GetItem(SID_DASH_LIST);
384         if (!pItem)
385             return;
386 
387         XLineDashItem aDashItem;
388         aDashItem.PutValue(rEvent.State, 0);
389         const XDash& rDash = aDashItem.GetDashValue();
390 
391         XDashListRef xLineStyleList = pItem->GetDashList();
392         for (tools::Long i = 0; i < xLineStyleList->Count(); ++i)
393         {
394             const XDashEntry* pEntry = xLineStyleList->GetDash(i);
395             const XDash& rEntry = pEntry->GetDash();
396             if (rDash == rEntry)
397             {
398                 m_nDashStyleIndex = i;
399                 break;
400             }
401         }
402     }
403     else if (rEvent.FeatureURL.Complete == ".uno:XLineStyle")
404     {
405         XLineStyleItem aLineStyleItem;
406         aLineStyleItem.PutValue(rEvent.State, 0);
407 
408         m_eXLS = aLineStyleItem.GetValue();
409     }
410 }
411 
GetStyleIndex() const412 int ToolboxButtonLineStyleUpdater::GetStyleIndex() const
413 {
414     int nRet;
415     switch (m_eXLS)
416     {
417         case css::drawing::LineStyle_NONE:
418             nRet = 0;
419             break;
420         case css::drawing::LineStyle_SOLID:
421             nRet = 1;
422             break;
423         default:
424             nRet = m_nDashStyleIndex + 2;
425             break;
426     }
427     return nRet;
428 }
429 }
430 
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
432