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
