xref: /core/svx/source/tbxctrls/tbxcolorupdate.cxx (revision bc91cc47)
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/bits/defines.h>
46 #include <frozen/bits/elsa_std.h>
47 #include <frozen/unordered_map.h>
48 
49 namespace svx
50 {
ToolboxButtonColorUpdaterBase(bool bWideButton,OUString aCommandLabel,OUString aCommandURL,sal_uInt16 nSlotId,css::uno::Reference<css::frame::XFrame> xFrame)51     ToolboxButtonColorUpdaterBase::ToolboxButtonColorUpdaterBase(bool bWideButton, OUString aCommandLabel,
52                                                                  OUString aCommandURL, sal_uInt16 nSlotId,
53                                                                  css::uno::Reference<css::frame::XFrame> xFrame)
54         : mbWideButton(bWideButton)
55         , mbWasHiContrastMode(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
56         , mnSlotId(nSlotId)
57         , maCurColor(COL_TRANSPARENT)
58         , meImageType(vcl::ImageType::Size16)
59         , maCommandLabel(std::move(aCommandLabel))
60         , maCommandURL(std::move(aCommandURL))
61         , mxFrame(std::move(xFrame))
62     {
63     }
64 
Init(sal_uInt16 nSlotId)65     void ToolboxButtonColorUpdaterBase::Init(sal_uInt16 nSlotId)
66     {
67         if (mbWideButton)
68         {
69             Update(COL_TRANSPARENT, true);
70             return;
71         }
72 
73         if (rtl::Reference xModel = dynamic_cast<SfxBaseModel*>(mxFrame->getController()->getModel().get()))
74         {
75             auto pDocSh = xModel->GetObjectShell();
76             StartListening(*pDocSh);
77             if (auto oColor = pDocSh->GetRecentColor(nSlotId))
78             {
79                 Update(*oColor);
80                 return;
81             }
82         }
83 
84         // tdf#72991 - remember last used color depending on slot id
85         const auto aSlotNamedColorMap = frozen::make_unordered_map<sal_uInt16, NamedColor>(
86             { { SID_ATTR_CHAR_COLOR,
87                 NamedColor(COL_DEFAULT_FONT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FONT)) },
88               { SID_ATTR_CHAR_COLOR2,
89                 NamedColor(COL_DEFAULT_FONT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FONT)) },
90               { SID_FRAME_LINECOLOR,
91                 NamedColor(COL_DEFAULT_FRAMELINE, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FRAMELINE)) },
92               { SID_ATTR_CHAR_COLOR_BACKGROUND,
93                 NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
94               { SID_ATTR_CHAR_BACK_COLOR,
95                 NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
96               { SID_BACKGROUND_COLOR,
97                 NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
98               { SID_TABLE_CELL_BACKGROUND_COLOR,
99                 NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)) },
100               { SID_ATTR_LINE_COLOR, NamedColor(COL_DEFAULT_SHAPE_STROKE,
101                                                 SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_STROKE)) },
102               { SID_ATTR_FILL_COLOR, NamedColor(COL_DEFAULT_SHAPE_FILLING,
103                                                 SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_FILLING)) }
104 
105             });
106 
107         const auto aIterator = aSlotNamedColorMap.find(nSlotId);
108         if (aIterator != aSlotNamedColorMap.end())
109         {
110             NamedColor aNamedColor(aIterator->second);
111             SvtViewOptions aViewOpt(EViewType::Dialog, u"ToolboxButtonColor"_ustr);
112             if (aViewOpt.Exists())
113             {
114                 css::uno::Any aUserItem = aViewOpt.GetUserItem(OUString::number(nSlotId));
115                 OUString aUserData;
116                 if (aUserItem >>= aUserData)
117                 {
118                     sal_Int32 nIdx = 0;
119                     aNamedColor.m_aName = o3tl::getToken(aUserData, 0, ';', nIdx);
120                     aNamedColor.m_aColor
121                         = Color(ColorTransparencyTag::ColorTransparency,
122                                 o3tl::toUInt32(o3tl::getToken(aUserData, 0, ';', nIdx)));
123                 }
124             }
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)152     void ToolboxButtonColorUpdaterBase::SetRecentColor(const NamedColor &rNamedColor)
153     {
154         if (rtl::Reference xModel = dynamic_cast<SfxBaseModel*>(mxFrame->getController()->getModel().get()))
155             xModel->GetObjectShell()->SetRecentColor(mnSlotId, rNamedColor);
156         else if (!mbWideButton)
157             Update(rNamedColor);
158     }
159 
VclToolboxButtonColorUpdater(sal_uInt16 nSlotId,ToolBoxItemId nTbxBtnId,ToolBox * pToolBox,bool bWideButton,const OUString & rCommandLabel,const OUString & rCommandURL,const css::uno::Reference<css::frame::XFrame> & rFrame)160     VclToolboxButtonColorUpdater::VclToolboxButtonColorUpdater(
161             sal_uInt16 nSlotId, ToolBoxItemId nTbxBtnId, ToolBox* pToolBox, bool bWideButton,
162             const OUString& rCommandLabel, const OUString& rCommandURL,
163             const css::uno::Reference<css::frame::XFrame>& rFrame)
164         : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, rCommandURL, nSlotId, rFrame)
165         , mnBtnId(nTbxBtnId)
166         , mpTbx(pToolBox)
167     {
168         Init(nSlotId);
169     }
170 
SetQuickHelpText(const OUString & rText)171     void VclToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
172     {
173         mpTbx->SetQuickHelpText(mnBtnId, rText);
174     }
175 
GetQuickHelpText() const176     OUString VclToolboxButtonColorUpdater::GetQuickHelpText() const
177     {
178         return mpTbx->GetQuickHelpText(mnBtnId);
179     }
180 
SetImage(VirtualDevice * pVirDev)181     void VclToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
182     {
183         GDIMetaFile* pMtf = pVirDev->GetConnectMetaFile();
184 
185         assert(pMtf && "should have been set in ToolboxButtonColorUpdaterBase::Update");
186 
187         pMtf->Stop();
188         pMtf->WindStart();
189 
190         Graphic aGraphic(*pMtf);
191 
192         mpTbx->SetItemImage(mnBtnId, Image(aGraphic.GetXGraphic()));
193     }
194 
CreateVirtualDevice() const195     VclPtr<VirtualDevice> VclToolboxButtonColorUpdater::CreateVirtualDevice() const
196     {
197         return VclPtr<VirtualDevice>::Create(*mpTbx->GetOutDev());
198     }
199 
GetImageSize() const200     vcl::ImageType VclToolboxButtonColorUpdater::GetImageSize() const
201     {
202         return mpTbx->GetImageSize();
203     }
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     {}
214 
Update(const NamedColor & rNamedColor)215     void ToolboxButtonColorUpdaterBase::Update(const NamedColor &rNamedColor)
216     {
217         Update(rNamedColor.m_aColor);
218 
219         // Also show the current color as QuickHelpText
220         OUString colorSuffix = u" (%1)"_ustr.replaceFirst("%1", rNamedColor.m_aName);
221         OUString colorHelpText = maCommandLabel + colorSuffix;
222         SetQuickHelpText(colorHelpText);
223     }
224 
Update(const Color & rColor,bool bForceUpdate)225     void ToolboxButtonColorUpdaterBase::Update(const Color& rColor, bool bForceUpdate)
226     {
227         vcl::ImageType eImageType = GetImageSize();
228 
229 #ifdef IOS // tdf#126966
230         eImageType = vcl::ImageType::Size32;
231 #endif
232 
233         const bool bSizeChanged = (meImageType != eImageType);
234         meImageType = eImageType;
235         const bool bDisplayModeChanged = (mbWasHiContrastMode != Application::GetSettings().GetStyleSettings().GetHighContrastMode());
236         Color aColor(rColor);
237 
238         // !!! #109290# Workaround for SetFillColor with COL_AUTO
239         if (aColor == COL_AUTO)
240             aColor = COL_TRANSPARENT;
241 
242         if ((maCurColor == aColor) && !bSizeChanged && !bDisplayModeChanged && !bForceUpdate)
243             return;
244 
245         auto xImage = vcl::CommandInfoProvider::GetXGraphicForCommand(maCommandURL, mxFrame, meImageType);
246         Image aImage(xImage);
247 
248         Size aItemSize = GetItemSize(aImage.GetSizePixel());
249         if (!aItemSize.Width() || !aItemSize.Height())
250             return;
251 
252         ScopedVclPtr<VirtualDevice> pVirDev(CreateVirtualDevice());
253         pVirDev->SetOutputSizePixel(aItemSize, /*bErase*/true, /*bAlphaMaskTransparent*/true);
254         maBmpSize = aItemSize;
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         if (maBmpSize.Width() == maBmpSize.Height())
267             // tdf#84985 align color bar with icon bottom edge; integer arithmetic e.g. 26 - 26/4 <> 26 * 3/4
268             maUpdRect = tools::Rectangle(Point( 0, maBmpSize.Height() - maBmpSize.Height() / 4), Size(maBmpSize.Width(), maBmpSize.Height() / 4));
269         else
270             maUpdRect = tools::Rectangle(Point( maBmpSize.Height() + 2, 2), Point(maBmpSize.Width() - 3, maBmpSize.Height() - 3));
271 
272         pVirDev->Push(vcl::PushFlags::CLIPREGION);
273 
274         // tdf#135121 don't include the part of the image which we will
275         // overwrite with the target color so that for the transparent color
276         // case the original background of the device is shown
277         vcl::Region aRegion(tools::Rectangle(Point(0, 0), maBmpSize));
278         aRegion.Exclude(maUpdRect);
279         pVirDev->SetClipRegion(aRegion);
280 
281         pVirDev->DrawImage(Point(0, 0), aImage);
282 
283         pVirDev->Pop();
284 
285         const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
286         mbWasHiContrastMode = rStyleSettings.GetHighContrastMode();
287 
288         if ((COL_TRANSPARENT != aColor) && (maBmpSize.Width() == maBmpSize.Height()))
289             pVirDev->SetLineColor(aColor);
290         else
291             pVirDev->SetLineColor(rStyleSettings.GetDisableColor());
292 
293         // use not only COL_TRANSPARENT for detection of transparence,
294         // but the method/way which is designed to do that
295         const bool bIsFullyTransparent(aColor.IsFullyTransparent());
296         maCurColor = aColor;
297 
298         if (bIsFullyTransparent)
299         {
300             pVirDev->SetFillColor();
301         }
302         else
303         {
304             pVirDev->SetFillColor(maCurColor);
305         }
306 
307         pVirDev->DrawRect(maUpdRect);
308 
309         SetImage(pVirDev.get());
310     }
311 
GetCurrentColorName() const312     OUString ToolboxButtonColorUpdaterBase::GetCurrentColorName() const
313     {
314         OUString sColorName = GetQuickHelpText();
315         // The obtained string is of format: color context (color name)
316         // Generate a substring which contains only the color name
317         sal_Int32 nStart = sColorName.indexOf('(');
318         sColorName = sColorName.copy( nStart + 1 );
319         sal_Int32 nLength = sColorName.getLength();
320         if(nLength > 0)
321             sColorName = sColorName.copy( 0, nLength - 1);
322         return sColorName;
323     }
324 
ToolboxButtonColorUpdater(sal_uInt16 nSlotId,const OUString & rTbxBtnId,weld::Toolbar * ptrTbx,bool bWideButton,const OUString & rCommandLabel,const css::uno::Reference<css::frame::XFrame> & rFrame)325     ToolboxButtonColorUpdater::ToolboxButtonColorUpdater(sal_uInt16 nSlotId, const OUString& rTbxBtnId, weld::Toolbar* ptrTbx, bool bWideButton,
326                                                          const OUString& rCommandLabel, const css::uno::Reference<css::frame::XFrame>& rFrame)
327         : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, rTbxBtnId, nSlotId, rFrame)
328         , msBtnId(rTbxBtnId)
329         , mpTbx(ptrTbx)
330     {
331         Init(nSlotId);
332     }
333 
SetQuickHelpText(const OUString & rText)334     void ToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
335     {
336         mpTbx->set_item_tooltip_text(msBtnId, rText);
337     }
338 
GetQuickHelpText() const339     OUString ToolboxButtonColorUpdater::GetQuickHelpText() const
340     {
341         return mpTbx->get_item_tooltip_text(msBtnId);
342     }
343 
SetImage(VirtualDevice * pVirDev)344     void ToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
345     {
346         mpTbx->set_item_image(msBtnId, pVirDev);
347     }
348 
CreateVirtualDevice() const349     VclPtr<VirtualDevice> ToolboxButtonColorUpdater::CreateVirtualDevice() const
350     {
351         return mpTbx->create_virtual_device();
352     }
353 
GetImageSize() const354     vcl::ImageType ToolboxButtonColorUpdater::GetImageSize() const
355     {
356         return mpTbx->get_icon_size();
357     }
358 
GetItemSize(const Size & rImageSize) const359     Size ToolboxButtonColorUpdater::GetItemSize(const Size& rImageSize) const
360     {
361         auto nWidth = rImageSize.Width();
362         if (mbWideButton)
363             nWidth = nWidth * 5;
364         return Size(nWidth, rImageSize.Height());
365     }
366 
ToolboxButtonLineStyleUpdater()367     ToolboxButtonLineStyleUpdater::ToolboxButtonLineStyleUpdater()
368         : m_eXLS(css::drawing::LineStyle_NONE)
369         , m_nDashStyleIndex(-1)
370     {
371     }
372 
Update(const com::sun::star::frame::FeatureStateEvent & rEvent)373     void ToolboxButtonLineStyleUpdater::Update(const com::sun::star::frame::FeatureStateEvent& rEvent)
374     {
375         if (rEvent.FeatureURL.Complete == ".uno:LineDash")
376         {
377             m_nDashStyleIndex = -1;
378 
379             SfxObjectShell* pSh = SfxObjectShell::Current();
380             if (!pSh)
381                 return;
382             const SvxDashListItem* pItem = pSh->GetItem( SID_DASH_LIST );
383             if (!pItem)
384                 return;
385 
386             XLineDashItem aDashItem;
387             aDashItem.PutValue(rEvent.State, 0);
388             const XDash& rDash = aDashItem.GetDashValue();
389 
390             XDashListRef xLineStyleList = pItem->GetDashList();
391             for (tools::Long i = 0; i < xLineStyleList->Count(); ++i)
392             {
393                 const XDashEntry* pEntry = xLineStyleList->GetDash(i);
394                 const XDash& rEntry = pEntry->GetDash();
395                 if (rDash == rEntry)
396                 {
397                     m_nDashStyleIndex = i;
398                     break;
399                 }
400             }
401         }
402         else if (rEvent.FeatureURL.Complete == ".uno:XLineStyle")
403         {
404             XLineStyleItem aLineStyleItem;
405             aLineStyleItem.PutValue(rEvent.State, 0);
406 
407             m_eXLS = aLineStyleItem.GetValue();
408         }
409     }
410 
GetStyleIndex() const411     int ToolboxButtonLineStyleUpdater::GetStyleIndex() const
412     {
413         int nRet;
414         switch (m_eXLS)
415         {
416             case css::drawing::LineStyle_NONE:
417                 nRet = 0;
418                 break;
419             case css::drawing::LineStyle_SOLID:
420                 nRet = 1;
421                 break;
422             default:
423                 nRet = m_nDashStyleIndex + 2;
424                 break;
425         }
426         return nRet;
427     }
428 }
429 
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
431