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