LibreOffice Module svx (master) 1
PaletteManager.cxx
Go to the documentation of this file.
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 <memory>
22
24#include <tools/urlobj.hxx>
25#include <osl/file.hxx>
27#include <sfx2/objsh.hxx>
28#include <svx/drawitem.hxx>
29#include <svx/strings.hrc>
30#include <svx/svxids.hrc>
31#include <svx/dialmgr.hxx>
32#include <tbxcolorupdate.hxx>
33#include <vcl/svapp.hxx>
34#include <vcl/settings.hxx>
36#include <stack>
37#include <set>
38#include <officecfg/Office/Common.hxx>
39#include <com/sun/star/frame/XDispatchProvider.hpp>
40#include <com/sun/star/frame/XDispatch.hpp>
41#include <com/sun/star/frame/Desktop.hpp>
42#include <com/sun/star/util/XURLTransformer.hpp>
43#include <com/sun/star/util/URLTransformer.hpp>
46#include <editeng/colritem.hxx>
47#include <editeng/memberids.h>
48
49#include <palettes.hxx>
50
51namespace
52{
53// Luminance modulation for the 6 effect presets.
54// 10000 is the default.
55sal_Int16 g_aLumMods[] = { 10000, 2000, 4000, 6000, 7500, 5000 };
56
57// Luminance offset for the 6 effect presets.
58// 0 is the default.
59sal_Int16 g_aLumOffs[] = { 0, 8000, 6000, 4000, 0, 0 };
60}
61
63 mnMaxRecentColors(Application::GetSettings().GetStyleSettings().GetColorValueSetColumnCount()),
64 mnNumOfPalettes(3),
65 mnCurrentPalette(0),
66 mnColorCount(0),
67 mpBtnUpdater(nullptr),
68 maColorSelectFunction(PaletteManager::DispatchColorCommand)
69{
71 if(pDocSh)
72 {
73 const SfxPoolItem* pItem = nullptr;
74 if( nullptr != ( pItem = pDocSh->GetItem(SID_COLOR_TABLE) ) )
75 pColorList = static_cast<const SvxColorListItem*>(pItem)->GetColorList();
76 }
77 if(!pColorList.is())
81
82}
83
85 : mnMaxRecentColors(pClone->mnMaxRecentColors)
86 , mnNumOfPalettes(pClone->mnNumOfPalettes)
87 , mnCurrentPalette(pClone->mnCurrentPalette)
88 , mnColorCount(pClone->mnColorCount)
89 , mpBtnUpdater(nullptr)
90 , pColorList(pClone->pColorList)
91 , maRecentColors(pClone->maRecentColors)
92 , maColorSelectFunction(PaletteManager::DispatchColorCommand)
93{
94 for (const auto& a : pClone->m_Palettes)
95 m_Palettes.emplace_back(a->Clone());
96}
97
99{
100 return new PaletteManager(this);
101}
102
104{
105}
106
108{
109 m_Palettes.clear();
110 OUString aPalPaths = SvtPathOptions().GetPalettePath();
111
112 std::stack<OUString> aDirs;
113 sal_Int32 nIndex = 0;
114 do
115 {
116 aDirs.push(aPalPaths.getToken(0, ';', nIndex));
117 }
118 while (nIndex >= 0);
119
120 std::set<OUString> aNames;
121 //try all entries palette path list user first, then
122 //system, ignoring duplicate file names
123 while (!aDirs.empty())
124 {
125 OUString aPalPath = aDirs.top();
126 aDirs.pop();
127
128 osl::Directory aDir(aPalPath);
129 osl::DirectoryItem aDirItem;
130 osl::FileStatus aFileStat( osl_FileStatus_Mask_FileName |
131 osl_FileStatus_Mask_FileURL |
132 osl_FileStatus_Mask_Type );
133 if( aDir.open() == osl::FileBase::E_None )
134 {
135 while( aDir.getNextItem(aDirItem) == osl::FileBase::E_None )
136 {
137 aDirItem.getFileStatus(aFileStat);
138 if(aFileStat.isRegular() || aFileStat.isLink())
139 {
140 OUString aFName = aFileStat.getFileName();
141 INetURLObject aURLObj( aFileStat.getFileURL() );
142 OUString aFNameWithoutExt = aURLObj.GetBase();
143 if (aNames.find(aFName) == aNames.end())
144 {
145 std::unique_ptr<Palette> pPalette;
146 if( aFName.endsWithIgnoreAsciiCase(".gpl") )
147 pPalette.reset(new PaletteGPL(aFileStat.getFileURL(), aFNameWithoutExt));
148 else if( aFName.endsWithIgnoreAsciiCase(".soc") )
149 pPalette.reset(new PaletteSOC(aFileStat.getFileURL(), aFNameWithoutExt));
150 else if ( aFName.endsWithIgnoreAsciiCase(".ase") )
151 pPalette.reset(new PaletteASE(aFileStat.getFileURL(), aFNameWithoutExt));
152
153 if( pPalette && pPalette->IsValid() )
154 m_Palettes.push_back( std::move(pPalette) );
155 aNames.insert(aFNameWithoutExt);
156 }
157 }
158 }
159 }
160 }
161}
162
164{
165 return mnCurrentPalette == mnNumOfPalettes - 2;
166}
167
168void PaletteManager::GetThemeIndexLumModOff(sal_uInt16 nItemId, sal_Int16& rThemeIndex,
169 sal_Int16& rLumMod, sal_Int16& rLumOff)
170{
171 // Each column is the same color with different effects.
172 rThemeIndex = nItemId % 12;
173
174 // Each row is the same effect with different colors.
175 rLumMod = g_aLumMods[nItemId / 12];
176 rLumOff = g_aLumOffs[nItemId / 12];
177}
178
180{
181 if( mnCurrentPalette == 0)
182 {
183 rColorSet.Clear();
184 css::uno::Sequence< sal_Int32 > CustomColorList( officecfg::Office::Common::UserColors::CustomColor::get() );
185 css::uno::Sequence< OUString > CustomColorNameList( officecfg::Office::Common::UserColors::CustomColorName::get() );
186 int nIx = 1;
187 for (int i = 0; i < CustomColorList.getLength(); ++i)
188 {
189 Color aColor(ColorTransparency, CustomColorList[i]);
190 rColorSet.InsertItem(nIx, aColor, CustomColorNameList[i]);
191 ++nIx;
192 }
193 }
194 else if (IsThemePaletteSelected())
195 {
196 SfxObjectShell* pObjectShell = SfxObjectShell::Current();
197 if (pObjectShell)
198 {
199 std::vector<Color> aColors = pObjectShell->GetThemeColors();
200 mnColorCount = aColors.size();
201 rColorSet.Clear();
202 if (aColors.size() >= 12)
203 {
204 std::vector<OUString> aEffectNames = {
205 SvxResId(RID_SVXSTR_THEME_EFFECT1), SvxResId(RID_SVXSTR_THEME_EFFECT2),
206 SvxResId(RID_SVXSTR_THEME_EFFECT3), SvxResId(RID_SVXSTR_THEME_EFFECT4),
207 SvxResId(RID_SVXSTR_THEME_EFFECT5),
208 };
209
210 std::vector<OUString> aColorNames = {
211 SvxResId(RID_SVXSTR_THEME_COLOR1), SvxResId(RID_SVXSTR_THEME_COLOR2),
212 SvxResId(RID_SVXSTR_THEME_COLOR3), SvxResId(RID_SVXSTR_THEME_COLOR4),
213 SvxResId(RID_SVXSTR_THEME_COLOR5), SvxResId(RID_SVXSTR_THEME_COLOR6),
214 SvxResId(RID_SVXSTR_THEME_COLOR7), SvxResId(RID_SVXSTR_THEME_COLOR8),
215 SvxResId(RID_SVXSTR_THEME_COLOR9), SvxResId(RID_SVXSTR_THEME_COLOR10),
216 SvxResId(RID_SVXSTR_THEME_COLOR11), SvxResId(RID_SVXSTR_THEME_COLOR12),
217 };
218
219 sal_uInt16 nItemId = 0;
220 // Each row is one effect type (no effect + each type).
221 for (size_t nEffect = 0; nEffect < aEffectNames.size() + 1; ++nEffect)
222 {
223 // Each column is one color type.
224 for (size_t nColor = 0; nColor < aColorNames.size(); ++nColor)
225 {
226 Color aColor = aColors[nColor];
227 aColor.ApplyLumModOff(g_aLumMods[nEffect], g_aLumOffs[nEffect]);
228 OUString aColorName;
229 if (nEffect == 0)
230 {
231 aColorName = aColorNames[nColor];
232 }
233 else
234 {
235 aColorName = aEffectNames[nEffect - 1].replaceAll("%1", aColorNames[nColor]);
236 }
237 rColorSet.InsertItem(nItemId++, aColor, aColorName);
238 }
239 }
240 }
241 }
242 }
243 else if( mnCurrentPalette == mnNumOfPalettes - 1 )
244 {
245 // Add doc colors to palette
247 if (pDocSh)
248 {
249 std::set<Color> aColors = pDocSh->GetDocColors();
250 mnColorCount = aColors.size();
251 rColorSet.Clear();
252 rColorSet.addEntriesForColorSet(aColors, Concat2View(SvxResId( RID_SVXSTR_DOC_COLOR_PREFIX ) + " ") );
253 }
254 }
255 else
256 {
257 m_Palettes[mnCurrentPalette - 1]->LoadColorSet( rColorSet );
258 mnColorCount = rColorSet.GetItemCount();
259 }
260}
261
263{
264 maRecentColors.clear();
265 rColorSet.Clear();
266 css::uno::Sequence< sal_Int32 > Colorlist(officecfg::Office::Common::UserColors::RecentColor::get());
267 css::uno::Sequence< OUString > ColorNamelist(officecfg::Office::Common::UserColors::RecentColorName::get());
268 int nIx = 1;
269 const bool bHasColorNames = Colorlist.getLength() == ColorNamelist.getLength();
270 for (int i = 0; i < Colorlist.getLength(); ++i)
271 {
272 Color aColor(ColorTransparency, Colorlist[i]);
273 OUString sColorName = bHasColorNames ? ColorNamelist[i] : ("#" + aColor.AsRGBHexString().toAsciiUpperCase());
274 maRecentColors.emplace_back(aColor, sColorName);
275 rColorSet.InsertItem(nIx, aColor, sColorName);
276 ++nIx;
277 }
278}
279
280std::vector<OUString> PaletteManager::GetPaletteList()
281{
282 std::vector<OUString> aPaletteNames
283 {
284 SvxResId( RID_SVXSTR_CUSTOM_PAL )
285 };
286 for (auto const& it : m_Palettes)
287 {
288 aPaletteNames.push_back( (*it).GetName() );
289 }
290 aPaletteNames.push_back(SvxResId(RID_SVXSTR_THEME_COLORS));
291 aPaletteNames.push_back( SvxResId ( RID_SVXSTR_DOC_COLORS ) );
292
293 return aPaletteNames;
294}
295
296void PaletteManager::SetPalette( sal_Int32 nPos )
297{
299 if( nPos != mnNumOfPalettes - 1 && nPos != 0)
300 {
304 auto name = GetPaletteName(); // may change pColorList
305 pColorList->SetName(name);
306 if(pColorList->Load())
307 {
309 if (pShell != nullptr)
310 {
311 SvxColorListItem aColorItem(pColorList, SID_COLOR_TABLE);
312 pShell->PutItem( aColorItem );
313 }
314 }
315 }
316 OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
317 if (aPaletteName != GetPaletteName())
318 {
319 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
320 officecfg::Office::Common::UserColors::PaletteName::set(GetPaletteName(), batch);
321 batch->commit();
322 }
323}
324
326{
327 return mnCurrentPalette;
328}
329
331{
332 std::vector<OUString> aNames(GetPaletteList());
334 {
336 if(pDocSh)
337 {
338 const SfxPoolItem* pItem = nullptr;
339 if( nullptr != ( pItem = pDocSh->GetItem(SID_COLOR_TABLE) ) )
340 pColorList = static_cast<const SvxColorListItem*>(pItem)->GetColorList();
341 }
342 }
343 return aNames[mnCurrentPalette];
344}
345
347{
348 if (mnCurrentPalette < m_Palettes.size() && mnCurrentPalette != 0)
349 return m_Palettes[mnCurrentPalette - 1]->GetPath();
350 else
351 return OUString();
352}
353
355{
356 return mnColorCount;
357}
358
360{
361 return maRecentColors.size();
362}
363
364void PaletteManager::AddRecentColor(const Color& rRecentColor, const OUString& rName, bool bFront)
365{
366 auto itColor = std::find_if(maRecentColors.begin(),
367 maRecentColors.end(),
368 [rRecentColor] (const NamedColor &a) { return a.first == rRecentColor; });
369 // if recent color to be added is already in list, remove it
370 if( itColor != maRecentColors.end() )
371 maRecentColors.erase( itColor );
372
373 if (maRecentColors.size() == mnMaxRecentColors)
374 maRecentColors.pop_back();
375 if (bFront)
376 maRecentColors.push_front(std::make_pair(rRecentColor, rName));
377 else
378 maRecentColors.emplace_back(rRecentColor, rName);
379 css::uno::Sequence< sal_Int32 > aColorList(maRecentColors.size());
380 auto aColorListRange = asNonConstRange(aColorList);
381 css::uno::Sequence< OUString > aColorNameList(maRecentColors.size());
382 auto aColorNameListRange = asNonConstRange(aColorNameList);
383 for (size_t i = 0; i < maRecentColors.size(); ++i)
384 {
385 aColorListRange[i] = static_cast<sal_Int32>(maRecentColors[i].first);
386 aColorNameListRange[i] = maRecentColors[i].second;
387 }
388 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
389 officecfg::Office::Common::UserColors::RecentColor::set(aColorList, batch);
390 officecfg::Office::Common::UserColors::RecentColorName::set(aColorNameList, batch);
391 batch->commit();
392}
393
395{
396 mpBtnUpdater = pBtnUpdater;
397}
398
400{
401 maColorSelectFunction = aColorSelectFunction;
402}
403
404void PaletteManager::PopupColorPicker(weld::Window* pParent, const OUString& aCommand, const Color& rInitialColor)
405{
406 // The calling object goes away during aColorDlg.Execute(), so we must copy this
407 OUString aCommandCopy = aCommand;
408 m_pColorDlg = std::make_unique<SvColorDialog>();
409 m_pColorDlg->SetColor(rInitialColor);
411 m_pColorDlg->ExecuteAsync(pParent, [this, aCommandCopy] (sal_Int32 nResult) {
412 if (nResult == RET_OK)
413 {
414 Color aLastColor = m_pColorDlg->GetColor();
415 OUString sColorName = "#" + aLastColor.AsRGBHexString().toAsciiUpperCase();
416 NamedColor aNamedColor = std::make_pair(aLastColor, sColorName);
417 if (mpBtnUpdater)
418 mpBtnUpdater->Update(aNamedColor);
419 AddRecentColor(aLastColor, sColorName);
421 }
422 });
423}
424
425void PaletteManager::DispatchColorCommand(const OUString& aCommand, const svx::NamedThemedColor& rColor)
426{
427 using namespace css;
428 using namespace css::uno;
429 using namespace css::frame;
430 using namespace css::beans;
431 using namespace css::util;
432
433 Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
434 Reference<XDesktop2> xDesktop = Desktop::create(xContext);
435 Reference<XFrame> xFrame(xDesktop->getCurrentFrame());
436 Reference<XDispatchProvider> xDispatchProvider(xFrame, UNO_QUERY);
437 if (!xDispatchProvider.is())
438 return;
439
440 INetURLObject aObj( aCommand );
441
442 std::vector<PropertyValue> aArgs{
443 comphelper::makePropertyValue(aObj.GetURLPath()+ ".Color", sal_Int32(rColor.m_aColor)),
444 };
445
446 if (rColor.m_nThemeIndex != -1)
447 {
448 model::ThemeColor aThemeColor;
450 if (rColor.m_nLumMod != 10000)
452 if (rColor.m_nLumMod != 0)
454
455 uno::Any aAny;
456 aAny <<= OStringToOUString(model::theme::convertToJSON(aThemeColor), RTL_TEXTENCODING_UTF8);
457
458 aArgs.push_back(comphelper::makePropertyValue(aObj.GetURLPath() + ".ThemeReferenceJSON", aAny));
459 }
460
462 aTargetURL.Complete = aCommand;
463 Reference<XURLTransformer> xURLTransformer(URLTransformer::create(comphelper::getProcessComponentContext()));
464 xURLTransformer->parseStrict(aTargetURL);
465
466 Reference<XDispatch> xDispatch = xDispatchProvider->queryDispatch(aTargetURL, OUString(), 0);
467 if (xDispatch.is())
468 {
470 if (xFrame->getContainerWindow().is())
471 xFrame->getContainerWindow()->setFocus();
472 }
473}
474
475/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::function< void(const OUString &, const svx::NamedThemedColor &)> ColorSelectFunction
Definition: Palette.hxx:51
std::pair< Color, OUString > NamedColor
Definition: Palette.hxx:30
OUString AsRGBHexString() const
void ApplyLumModOff(sal_Int16 nMod, sal_Int16 nOff)
OUString GetURLPath(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetBase() const
tools::Long GetColorCount() const
const sal_uInt16 mnMaxRecentColors
void SetColorSelectFunction(const ColorSelectFunction &aColorSelectFunction)
void PopupColorPicker(weld::Window *pParent, const OUString &aCommand, const Color &rInitialColor)
std::vector< OUString > GetPaletteList()
svx::ToolboxButtonColorUpdaterBase * mpBtnUpdater
static void GetThemeIndexLumModOff(sal_uInt16 nItemId, sal_Int16 &rThemeIndex, sal_Int16 &rLumMod, sal_Int16 &rLumOff)
PaletteManager * Clone() const
void ReloadColorSet(SvxColorValueSet &rColorSet)
tools::Long mnColorCount
tools::Long GetRecentColorCount() const
std::deque< NamedColor > maRecentColors
sal_uInt16 mnNumOfPalettes
XColorListRef pColorList
void ReloadRecentColorSet(SvxColorValueSet &rColorSet)
sal_uInt16 mnCurrentPalette
ColorSelectFunction maColorSelectFunction
std::vector< std::unique_ptr< Palette > > m_Palettes
static void DispatchColorCommand(const OUString &aCommand, const svx::NamedThemedColor &rColor)
std::unique_ptr< SvColorDialog > m_pColorDlg
void SetPalette(sal_Int32 nPos)
OUString GetPaletteName()
bool IsThemePaletteSelected() const
OUString GetSelectedPalettePath()
void SetBtnUpdater(svx::ToolboxButtonColorUpdaterBase *pBtnUpdater)
sal_Int32 GetPalette() const
void AddRecentColor(const Color &rRecentColor, const OUString &rColorName, bool bFront=true)
virtual std::vector< Color > GetThemeColors()
virtual std::set< Color > GetDocColors()
static SAL_WARN_UNUSED_RESULT SfxObjectShell * Current()
void PutItem(const SfxPoolItem &rItem)
const SfxPoolItem * GetItem(sal_uInt16 nSlotId) const
const OUString & GetPalettePath() const
void addEntriesForColorSet(const std::set< Color > &rColorSet, std::u16string_view rNamePrefix)
void InsertItem(sal_uInt16 nItemId, const Image &rImage)
size_t GetItemCount() const
void Clear()
static XColorListRef CreateStdColorList()
Definition: xtabcolr.cxx:31
static XColorListRef AsColorList(rtl::Reference< XPropertyList > const &plist)
Definition: xtable.hxx:378
static XPropertyListRef CreatePropertyListFromURL(XPropertyListType t, std::u16string_view rUrl)
Definition: xtable.cxx:354
static std::shared_ptr< ConfigurationChanges > create()
void addTransformation(Transformation const &rTransform)
void setType(ThemeColorType eType)
helper class to update a color in a toolbox button image
void Update(const NamedColor &rNamedColor)
ColorTransparency
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
Reference< XDispatch > xDispatch
const char * name
sal_Int32 nIndex
uno_Any a
sal_uInt16 nPos
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Reference< XComponentContext > getProcessComponentContext()
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
OString convertToJSON(model::ThemeColor const &rThemeColor)
constexpr ThemeColorType convertToThemeColorType(sal_Int32 nIndex)
long Long
A color with an optional name and other theming-related properties.
Definition: Palette.hxx:38
static NamedThemedColor FromNamedColor(const NamedColor &rNamedColor)
Definition: Palette.cxx:384
sal_Int16 m_nLumMod
Definition: Palette.hxx:42
sal_Int16 m_nThemeIndex
Definition: Palette.hxx:41
sal_Int16 m_nLumOff
Definition: Palette.hxx:43
Reference< XFrame > xFrame
OUString aCommand
OUString aTargetURL
RET_OK