xref: /core/cui/source/options/optaboutconfig.cxx (revision ecf6f6f2)
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 
10 #include "optaboutconfig.hxx"
11 #include <o3tl/safeint.hxx>
12 #include <o3tl/string_view.hxx>
13 
14 #include <com/sun/star/beans/NamedValue.hpp>
15 #include <com/sun/star/beans/PropertyAttribute.hpp>
16 #include <com/sun/star/beans/UnknownPropertyException.hpp>
17 #include <com/sun/star/beans/XPropertySetInfo.hpp>
18 #include <com/sun/star/configuration/ReadWriteAccess.hpp>
19 #include <com/sun/star/configuration/XDocumentation.hpp>
20 #include <com/sun/star/configuration/theDefaultProvider.hpp>
21 #include <com/sun/star/container/XHierarchicalName.hpp>
22 #include <com/sun/star/container/XNameAccess.hpp>
23 #include <com/sun/star/container/XNameReplace.hpp>
24 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 #include <com/sun/star/uno/Reference.hxx>
26 #include <com/sun/star/uno/Type.hxx>
27 #include <com/sun/star/uno/TypeClass.hpp>
28 #include <com/sun/star/util/InvalidStateException.hpp>
29 #include <com/sun/star/util/SearchAlgorithms2.hpp>
30 #include <com/sun/star/util/SearchFlags.hpp>
31 #include <com/sun/star/util/XChangesBatch.hpp>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <cppu/unotype.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <sal/log.hxx>
38 #include <unotools/textsearch.hxx>
39 #include <utility>
40 #include <vcl/event.hxx>
41 
42 #include <dialmgr.hxx>
43 #include <strings.hrc>
44 
45 #include <algorithm>
46 #include <memory>
47 #include <vector>
48 
49 using namespace ::com::sun::star;
50 using namespace com::sun::star::uno;
51 using namespace com::sun::star::container;
52 
53 struct Prop_Impl
54 {
55     OUString Name;
56     OUString Property;
57     Any Value;
58 
Prop_ImplProp_Impl59     Prop_Impl(OUString sName, OUString sProperty, Any aValue)
60         : Name(std::move(sName))
61         , Property(std::move(sProperty))
62         , Value(std::move(aValue))
63     {
64     }
65 };
66 
67 struct UserData
68 {
69     bool bIsPropertyPath;
70     bool bIsReadOnly;
71     bool bWasModified;
72     OUString sPropertyPath;
73     Any aPropertyValue;
74     OUString sTooltip;
75     int aLineage;
76     Reference<XNameAccess> aXNameAccess;
77 
UserDataUserData78     explicit UserData(OUString aPropertyPath, Any aPropValue, OUString aTooltip, bool isReadOnly,
79                       bool wasModified)
80         : bIsPropertyPath(true)
81         , bIsReadOnly(isReadOnly)
82         , bWasModified(wasModified)
83         , sPropertyPath(std::move(aPropertyPath))
84         , aPropertyValue(aPropValue)
85         , sTooltip(std::move(aTooltip))
86         , aLineage(0)
87     {
88     }
89 
UserDataUserData90     explicit UserData(Reference<XNameAccess> const& rXNameAccess, int rIndex)
91         : bIsPropertyPath(false)
92         , bIsReadOnly(false)
93         , bWasModified(false)
94         , aLineage(rIndex)
95         , aXNameAccess(rXNameAccess)
96     {
97     }
98 };
99 
CuiAboutConfigTabPage(weld::Window * pParent)100 CuiAboutConfigTabPage::CuiAboutConfigTabPage(weld::Window* pParent)
101     : GenericDialogController(pParent, u"cui/ui/aboutconfigdialog.ui"_ustr, u"AboutConfig"_ustr)
102     , m_xResetBtn(m_xBuilder->weld_button(u"reset"_ustr))
103     , m_xEditBtn(m_xBuilder->weld_button(u"edit"_ustr))
104     , m_xSearchBtn(m_xBuilder->weld_button(u"searchButton"_ustr))
105     , m_xModifiedCheckBtn(m_xBuilder->weld_check_button(u"modifiedButton"_ustr))
106     , m_xSearchEdit(m_xBuilder->weld_entry(u"searchEntry"_ustr))
107     , m_xPrefBox(m_xBuilder->weld_tree_view(u"preferences"_ustr))
108     , m_xScratchIter(m_xPrefBox->make_iterator())
109     , m_bSorted(false)
110 {
111     m_xPrefBox->set_size_request(m_xPrefBox->get_approximate_digit_width() * 100,
112                                  m_xPrefBox->get_height_rows(23));
113     m_xPrefBox->connect_column_clicked(LINK(this, CuiAboutConfigTabPage, HeaderBarClick));
114 
115     m_xEditBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, StandardHdl_Impl));
116     m_xResetBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, ResetBtnHdl_Impl));
117     m_xPrefBox->connect_row_activated(LINK(this, CuiAboutConfigTabPage, DoubleClickHdl_Impl));
118     m_xPrefBox->connect_expanding(LINK(this, CuiAboutConfigTabPage, ExpandingHdl_Impl));
119     m_xSearchBtn->connect_clicked(LINK(this, CuiAboutConfigTabPage, SearchHdl_Impl));
120     m_xModifiedCheckBtn->connect_toggled(LINK(this, CuiAboutConfigTabPage, ModifiedHdl_Impl));
121 
122     m_options.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
123     m_options.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
124     m_options.searchFlag
125         |= (util::SearchFlags::REG_NOT_BEGINOFLINE | util::SearchFlags::REG_NOT_ENDOFLINE);
126 
127     float fWidth = m_xPrefBox->get_approximate_digit_width();
128     std::vector<int> aWidths{ o3tl::narrowing<int>(fWidth * 65), o3tl::narrowing<int>(fWidth * 20),
129                               o3tl::narrowing<int>(fWidth * 8) };
130     m_xPrefBox->set_column_fixed_widths(aWidths);
131 
132     m_xPrefBox->connect_query_tooltip(LINK(this, CuiAboutConfigTabPage, QueryTooltip));
133 }
134 
IMPL_LINK(CuiAboutConfigTabPage,QueryTooltip,const weld::TreeIter &,rIter,OUString)135 IMPL_LINK(CuiAboutConfigTabPage, QueryTooltip, const weld::TreeIter&, rIter, OUString)
136 {
137     UserData* pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(rIter));
138     OUStringBuffer ret;
139     if (pUserData && pUserData->bIsReadOnly)
140     {
141         ret.append(CuiResId(RID_CUISTR_OPT_READONLY));
142     }
143     if (pUserData && !pUserData->sTooltip.isEmpty())
144     {
145         if (pUserData->bIsReadOnly)
146             ret.append("\n\n");
147         ret.append(pUserData->sTooltip);
148     }
149 
150     return ret.makeStringAndClear();
151 }
152 
IMPL_LINK(CuiAboutConfigTabPage,HeaderBarClick,int,nColumn,void)153 IMPL_LINK(CuiAboutConfigTabPage, HeaderBarClick, int, nColumn, void)
154 {
155     if (!m_bSorted)
156     {
157         m_xPrefBox->make_sorted();
158         m_bSorted = true;
159     }
160 
161     bool bSortAtoZ = m_xPrefBox->get_sort_order();
162 
163     //set new arrow positions in headerbar
164     if (nColumn == m_xPrefBox->get_sort_column())
165     {
166         bSortAtoZ = !bSortAtoZ;
167         m_xPrefBox->set_sort_order(bSortAtoZ);
168     }
169     else
170     {
171         int nOldSortColumn = m_xPrefBox->get_sort_column();
172         if (nOldSortColumn != -1)
173             m_xPrefBox->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
174         m_xPrefBox->set_sort_column(nColumn);
175     }
176 
177     if (nColumn != -1)
178     {
179         //sort lists
180         m_xPrefBox->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
181     }
182 }
183 
IMPL_STATIC_LINK_NOARG(CuiAboutConfigTabPage,ValidNameHdl,SvxNameDialog &,bool)184 IMPL_STATIC_LINK_NOARG(CuiAboutConfigTabPage, ValidNameHdl, SvxNameDialog&, bool)
185 {
186     // Allow empty value
187     return true;
188 }
189 
~CuiAboutConfigTabPage()190 CuiAboutConfigTabPage::~CuiAboutConfigTabPage() {}
191 
InsertEntry(const OUString & rPropertyPath,Any aPropertyValue,const OUString & rProp,const OUString & rStatus,const OUString & rType,const OUString & rValue,const OUString & rTooltip,const weld::TreeIter * pParentEntry,bool bInsertToPrefBox,bool bIsReadOnly,bool bWasModified)192 void CuiAboutConfigTabPage::InsertEntry(const OUString& rPropertyPath, Any aPropertyValue,
193                                         const OUString& rProp, const OUString& rStatus,
194                                         const OUString& rType, const OUString& rValue,
195                                         const OUString& rTooltip,
196                                         const weld::TreeIter* pParentEntry, bool bInsertToPrefBox,
197                                         bool bIsReadOnly, bool bWasModified)
198 {
199     bool bOnlyModified = m_xModifiedCheckBtn->get_active();
200     if (bOnlyModified && !bWasModified)
201         return;
202 
203     m_vectorUserData.push_back(std::make_unique<UserData>(rPropertyPath, aPropertyValue, rTooltip,
204                                                           bIsReadOnly, bWasModified));
205     if (bInsertToPrefBox)
206     {
207         OUString sId(weld::toId(m_vectorUserData.back().get()));
208         m_xPrefBox->insert(pParentEntry, -1, &rProp, &sId, nullptr, nullptr, false,
209                            m_xScratchIter.get());
210         m_xPrefBox->set_text(*m_xScratchIter, rStatus, 1);
211         m_xPrefBox->set_text(*m_xScratchIter, rType, 2);
212         m_xPrefBox->set_text(*m_xScratchIter, rValue, 3);
213         m_xPrefBox->set_text_emphasis(*m_xScratchIter, bWasModified, -1);
214         m_xPrefBox->set_sensitive(*m_xScratchIter, !bIsReadOnly, -1);
215     }
216     else
217     {
218         m_prefBoxEntries.push_back(
219             { rProp, rStatus, rType, rValue, m_vectorUserData.back().get() });
220     }
221 }
222 
InputChanged()223 void CuiAboutConfigTabPage::InputChanged()
224 {
225     weld::WaitObject aWait(m_xDialog.get());
226 
227     m_xPrefBox->hide();
228     m_xPrefBox->clear();
229     m_xPrefBox->freeze();
230 
231     if (m_bSorted)
232         m_xPrefBox->make_unsorted();
233 
234     if (m_xSearchEdit->get_text().isEmpty())
235     {
236         m_xPrefBox->clear();
237         Reference<XNameAccess> xConfigAccess = getConfigAccess(u"/"_ustr, false);
238         FillItems(xConfigAccess);
239     }
240     else
241     {
242         m_options.searchString = m_xSearchEdit->get_text();
243         utl::TextSearch textSearch(m_options);
244         for (auto const& it : m_prefBoxEntries)
245         {
246             sal_Int32 endPos, startPos = 0;
247 
248             for (size_t i = 0; i < 5; ++i)
249             {
250                 OUString scrTxt;
251 
252                 if (i == 0)
253                     scrTxt = it.pUserData->sPropertyPath;
254                 else if (i == 1)
255                     scrTxt = it.sProp;
256                 else if (i == 2)
257                     scrTxt = it.sStatus;
258                 else if (i == 3)
259                     scrTxt = it.sType;
260                 else if (i == 4)
261                     scrTxt = it.sValue;
262 
263                 endPos = scrTxt.getLength();
264                 if (textSearch.SearchForward(scrTxt, &startPos, &endPos))
265                 {
266                     InsertEntry(it);
267                     break;
268                 }
269             }
270         }
271     }
272 
273     m_xPrefBox->thaw();
274     if (m_bSorted)
275         m_xPrefBox->make_sorted();
276 
277     m_xPrefBox->all_foreach([this](weld::TreeIter& rEntry) {
278         m_xPrefBox->expand_row(rEntry);
279         return false;
280     });
281     m_xPrefBox->show();
282 }
283 
Reset()284 void CuiAboutConfigTabPage::Reset()
285 {
286     weld::WaitObject aWait(m_xDialog.get());
287 
288     m_xPrefBox->clear();
289     m_vectorOfModified.clear();
290     if (m_bSorted)
291     {
292         m_xPrefBox->set_sort_indicator(TRISTATE_INDET, m_xPrefBox->get_sort_column());
293         m_xPrefBox->make_unsorted();
294         m_bSorted = false;
295     }
296     m_prefBoxEntries.clear();
297     m_modifiedPrefBoxEntries.clear();
298 
299     m_xPrefBox->freeze();
300     Reference<XNameAccess> xConfigAccess = getConfigAccess(u"/"_ustr, false);
301     //Load all XNameAccess to m_prefBoxEntries
302     FillItems(xConfigAccess, nullptr, 0, true);
303     //Load xConfigAccess' children to m_prefBox
304     FillItems(xConfigAccess);
305     m_xPrefBox->thaw();
306 }
307 
FillItemSet()308 void CuiAboutConfigTabPage::FillItemSet()
309 {
310     std::vector<std::shared_ptr<Prop_Impl>>::iterator pIter;
311     for (pIter = m_vectorOfModified.begin(); pIter != m_vectorOfModified.end(); ++pIter)
312     {
313         Reference<XNameAccess> xUpdateAccess = getConfigAccess((*pIter)->Name, true);
314         Reference<XNameReplace> xNameReplace(xUpdateAccess, UNO_QUERY_THROW);
315 
316         xNameReplace->replaceByName((*pIter)->Property, (*pIter)->Value);
317 
318         Reference<util::XChangesBatch> xChangesBatch(xUpdateAccess, UNO_QUERY_THROW);
319         xChangesBatch->commitChanges();
320     }
321 }
322 
323 namespace
324 {
lcl_StringListToString(const uno::Sequence<OUString> & seq)325 OUString lcl_StringListToString(const uno::Sequence<OUString>& seq)
326 {
327     OUStringBuffer sBuffer;
328     for (sal_Int32 i = 0; i != seq.getLength(); ++i)
329     {
330         if (i != 0)
331             sBuffer.append(",");
332         sBuffer.append(seq[i]);
333     }
334     return sBuffer.makeStringAndClear();
335 }
336 
lcl_IntListToString(const uno::Sequence<sal_Int16> & seq)337 OUString lcl_IntListToString(const uno::Sequence<sal_Int16>& seq)
338 {
339     OUStringBuffer sBuffer;
340     for (sal_Int32 i = 0; i != seq.getLength(); ++i)
341     {
342         if (i != 0)
343             sBuffer.append(",");
344         sBuffer.append(OUString::number(seq[i]));
345     }
346     return sBuffer.makeStringAndClear();
347 }
348 
lcl_IntListToString(const uno::Sequence<sal_Int32> & seq)349 OUString lcl_IntListToString(const uno::Sequence<sal_Int32>& seq)
350 {
351     OUStringBuffer sBuffer;
352     for (sal_Int32 i = 0; i != seq.getLength(); ++i)
353     {
354         if (i != 0)
355             sBuffer.append(",");
356         sBuffer.append(OUString::number(seq[i]));
357     }
358     return sBuffer.makeStringAndClear();
359 }
360 
lcl_IntListToString(const uno::Sequence<sal_Int64> & seq)361 OUString lcl_IntListToString(const uno::Sequence<sal_Int64>& seq)
362 {
363     OUStringBuffer sBuffer;
364     for (sal_Int32 i = 0; i != seq.getLength(); ++i)
365     {
366         if (i != 0)
367             sBuffer.append(",");
368         sBuffer.append(OUString::number(seq[i]));
369     }
370     return sBuffer.makeStringAndClear();
371 }
372 
lcl_DoubleListToString(const uno::Sequence<double> & seq)373 OUString lcl_DoubleListToString(const uno::Sequence<double>& seq)
374 {
375     OUStringBuffer sBuffer;
376     for (sal_Int32 i = 0; i != seq.getLength(); ++i)
377     {
378         if (i != 0)
379             sBuffer.append(",");
380         sBuffer.append(OUString::number(seq[i]));
381     }
382     return sBuffer.makeStringAndClear();
383 }
384 }
385 
FillItems(const Reference<XNameAccess> & xNameAccess,const weld::TreeIter * pParentEntry,int lineage,bool bLoadAll)386 void CuiAboutConfigTabPage::FillItems(const Reference<XNameAccess>& xNameAccess,
387                                       const weld::TreeIter* pParentEntry, int lineage,
388                                       bool bLoadAll)
389 {
390     OUString sPath
391         = Reference<XHierarchicalName>(xNameAccess, uno::UNO_QUERY_THROW)->getHierarchicalName();
392     const uno::Sequence<OUString> seqItems = xNameAccess->getElementNames();
393     for (const OUString& item : seqItems)
394     {
395         Any aNode = xNameAccess->getByName(item);
396 
397         bool bNotLeaf = false;
398 
399         Reference<XNameAccess> xNextNameAccess;
400         try
401         {
402             xNextNameAccess.set(aNode, uno::UNO_QUERY);
403             bNotLeaf = xNextNameAccess.is();
404         }
405         catch (const RuntimeException&)
406         {
407             TOOLS_WARN_EXCEPTION("cui.options", "CuiAboutConfigTabPage");
408         }
409 
410         if (bNotLeaf)
411         {
412             if (bLoadAll)
413                 FillItems(xNextNameAccess, nullptr, lineage + 1, true);
414             else
415             {
416                 // not leaf node
417                 m_vectorUserData.push_back(
418                     std::make_unique<UserData>(xNextNameAccess, lineage + 1));
419                 OUString sId(weld::toId(m_vectorUserData.back().get()));
420 
421                 m_xPrefBox->insert(pParentEntry, -1, &item, &sId, nullptr, nullptr, true,
422                                    m_xScratchIter.get());
423                 // Necessary, without this the selection line will be truncated.
424                 m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 1);
425                 m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 2);
426                 m_xPrefBox->set_text(*m_xScratchIter, u""_ustr, 3);
427                 m_xPrefBox->set_sensitive(*m_xScratchIter, true);
428             }
429         }
430         else
431         {
432             // leaf node
433             OUString sPropertyName = item;
434             auto it = std::find_if(m_modifiedPrefBoxEntries.begin(), m_modifiedPrefBoxEntries.end(),
435                                    [&sPath, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
436                                        return rEntry.pUserData->sPropertyPath == sPath
437                                               && rEntry.sStatus == sPropertyName;
438                                    });
439 
440             css::uno::Reference<css::configuration::XReadWriteAccess> m_xReadWriteAccess;
441             m_xReadWriteAccess = css::configuration::ReadWriteAccess::create(
442                 ::comphelper::getProcessComponentContext(), u"*"_ustr);
443             beans::Property aProperty;
444             bool bReadOnly = false;
445             OUString sFullPath(sPath + "/" + sPropertyName);
446             try
447             {
448                 aProperty = m_xReadWriteAccess->getPropertyByHierarchicalName(sFullPath);
449                 bReadOnly = (aProperty.Attributes & beans::PropertyAttribute::READONLY) != 0;
450             }
451             catch (css::beans::UnknownPropertyException)
452             {
453                 SAL_WARN("cui.options", "unknown property: " << sFullPath);
454             }
455 
456             OUString sTooltip;
457             OUString sType;
458             bool bWasModified = false;
459             css::uno::Type aType = cppu::UnoType<void>::get();
460             OUString sDynamicType = aNode.getValueTypeName();
461             try
462             {
463                 Reference<configuration::XDocumentation> xDocumentation(xNameAccess,
464                                                                         UNO_QUERY_THROW);
465                 sTooltip
466                     = xDocumentation->getDescriptionByHierarchicalName(sPath + "/" + sPropertyName);
467                 aType = xDocumentation->getTypeByHierarchicalName(sFullPath);
468                 bWasModified = xDocumentation->getModifiedByHierarchicalName(sFullPath);
469             }
470             catch (css::container::NoSuchElementException)
471             {
472             }
473             catch (css::util::InvalidStateException)
474             {
475             }
476 
477             OUStringBuffer sValue;
478 
479             // Fall back to dynamic type when this is empty
480             if (aType == cppu::UnoType<void>::get() && sDynamicType != "void")
481             {
482                 if (sDynamicType == "boolean")
483                     aType = cppu::UnoType<sal_Bool>::get();
484                 else if (sDynamicType == "short")
485                     aType = cppu::UnoType<sal_Int16>::get();
486                 else if (sDynamicType == "long")
487                     aType = cppu::UnoType<sal_Int32>::get();
488                 else if (sDynamicType == "hyper")
489                     aType = cppu::UnoType<sal_Int64>::get();
490                 else if (sDynamicType == "double")
491                     aType = cppu::UnoType<double>::get();
492                 else if (sDynamicType == "string")
493                     aType = cppu::UnoType<OUString>::get();
494                 else if (sDynamicType == "[]byte")
495                     aType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
496                 else if (sDynamicType == "[]boolean")
497                     aType = cppu::UnoType<css::uno::Sequence<sal_Bool>>::get();
498                 else if (sDynamicType == "[]short")
499                     aType = cppu::UnoType<css::uno::Sequence<sal_Int16>>::get();
500                 else if (sDynamicType == "[]long")
501                     aType = cppu::UnoType<css::uno::Sequence<sal_Int32>>::get();
502                 else if (sDynamicType == "[]hyper")
503                     aType = cppu::UnoType<css::uno::Sequence<sal_Int64>>::get();
504                 else if (sDynamicType == "[]double")
505                     aType = cppu::UnoType<css::uno::Sequence<double>>::get();
506                 else if (sDynamicType == "[]string")
507                     aType = cppu::UnoType<css::uno::Sequence<OUString>>::get();
508                 else if (sDynamicType == "[][]byte")
509                     aType = cppu::UnoType<css::uno::Sequence<css::uno::Sequence<sal_Int8>>>::get();
510             }
511 
512             if (it != m_modifiedPrefBoxEntries.end())
513                 sValue = it->sValue;
514             else
515             {
516                 bool bHasValue = sDynamicType != "void";
517                 if (aType == cppu::UnoType<sal_Bool>::get())
518                 {
519                     if (bHasValue)
520                         sValue = OUString::boolean(aNode.get<bool>());
521                     sType = "boolean";
522                 }
523                 else if (aType == cppu::UnoType<sal_Int16>::get())
524                 {
525                     if (bHasValue)
526                         sValue = OUString::number(aNode.get<sal_Int16>());
527                     sType = "short";
528                 }
529                 else if (aType == cppu::UnoType<sal_Int32>::get())
530                 {
531                     if (bHasValue)
532                         sValue = OUString::number(aNode.get<sal_Int32>());
533                     sType = "int";
534                 }
535                 else if (aType == cppu::UnoType<sal_Int64>::get())
536                 {
537                     if (bHasValue)
538                         sValue = OUString::number(aNode.get<sal_Int64>());
539                     sType = "long";
540                 }
541                 else if (aType == cppu::UnoType<double>::get())
542                 {
543                     if (bHasValue)
544                         sValue = OUString::number(aNode.get<double>());
545                     sType = "double";
546                 }
547                 else if (aType == cppu::UnoType<OUString>::get())
548                 {
549                     if (bHasValue)
550                         sValue = aNode.get<OUString>();
551                     sType = "string";
552                 }
553                 else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int8>>::get())
554                 {
555                     if (bHasValue)
556                     {
557                         const uno::Sequence<sal_Int8> seq = aNode.get<uno::Sequence<sal_Int8>>();
558                         for (sal_Int8 j : seq)
559                         {
560                             OUString s = OUString::number(static_cast<sal_uInt8>(j), 16);
561                             if (s.getLength() == 1)
562                             {
563                                 sValue.append("0");
564                             }
565                             sValue.append(s.toAsciiUpperCase());
566                         }
567                     }
568                     sType = "hexBinary";
569                 }
570                 else if (aType == cppu::UnoType<css::uno::Sequence<sal_Bool>>::get())
571                 {
572                     if (bHasValue)
573                     {
574                         uno::Sequence<sal_Bool> seq = aNode.get<uno::Sequence<sal_Bool>>();
575                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
576                         {
577                             if (j != 0)
578                             {
579                                 sValue.append(",");
580                             }
581                             sValue.append(OUString::boolean(seq[j]));
582                         }
583                     }
584                     sType = "boolean-list";
585                 }
586                 else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int16>>::get())
587                 {
588                     if (bHasValue)
589                     {
590                         uno::Sequence<sal_Int16> seq = aNode.get<uno::Sequence<sal_Int16>>();
591                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
592                         {
593                             if (j != 0)
594                             {
595                                 sValue.append(",");
596                             }
597                             sValue.append(static_cast<sal_Int32>(seq[j]));
598                         }
599                     }
600                     sType = "short-list";
601                 }
602                 else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int32>>::get())
603                 {
604                     if (bHasValue)
605                     {
606                         uno::Sequence<sal_Int32> seq = aNode.get<uno::Sequence<sal_Int32>>();
607                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
608                         {
609                             if (j != 0)
610                             {
611                                 sValue.append(",");
612                             }
613                             sValue.append(seq[j]);
614                         }
615                     }
616                     sType = "int-list";
617                 }
618                 else if (aType == cppu::UnoType<css::uno::Sequence<sal_Int64>>::get())
619                 {
620                     if (bHasValue)
621                     {
622                         uno::Sequence<sal_Int64> seq = aNode.get<uno::Sequence<sal_Int64>>();
623                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
624                         {
625                             if (j != 0)
626                             {
627                                 sValue.append(",");
628                             }
629                             sValue.append(seq[j]);
630                         }
631                     }
632                     sType = "long-list";
633                 }
634                 else if (aType == cppu::UnoType<css::uno::Sequence<double>>::get())
635                 {
636                     if (bHasValue)
637                     {
638                         uno::Sequence<double> seq = aNode.get<uno::Sequence<double>>();
639                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
640                         {
641                             if (j != 0)
642                             {
643                                 sValue.append(",");
644                             }
645                             sValue.append(seq[j]);
646                         }
647                     }
648                     sType = "double-list";
649                 }
650                 else if (aType == cppu::UnoType<css::uno::Sequence<OUString>>::get())
651                 {
652                     if (bHasValue)
653                         sValue = lcl_StringListToString(aNode.get<uno::Sequence<OUString>>());
654                     sType = "string-list";
655                 }
656                 else if (aType
657                          == cppu::UnoType<css::uno::Sequence<css::uno::Sequence<sal_Int8>>>::get())
658                 {
659                     if (bHasValue)
660                     {
661                         const uno::Sequence<uno::Sequence<sal_Int8>> seq
662                             = aNode.get<uno::Sequence<uno::Sequence<sal_Int8>>>();
663                         for (sal_Int32 j = 0; j != seq.getLength(); ++j)
664                         {
665                             if (j != 0)
666                             {
667                                 sValue.append(",");
668                             }
669                             for (sal_Int8 k : seq[j])
670                             {
671                                 OUString s = OUString::number(static_cast<sal_uInt8>(k), 16);
672                                 if (s.getLength() == 1)
673                                 {
674                                     sValue.append("0");
675                                 }
676                                 sValue.append(s.toAsciiUpperCase());
677                             }
678                         }
679                     }
680                     sType = "hexBinary-list";
681                 }
682                 else
683                 {
684                     SAL_INFO("cui.options", "path \"" << sPath << "\" member " << item
685                                                       << " of unsupported type " << sType);
686                     continue;
687                 }
688             }
689 
690             //Short name
691             int index = 0;
692             for (int j = 1; j < lineage; ++j)
693                 index = sPath.indexOf("/", index + 1);
694 
695             InsertEntry(sPath, aNode, sPath.copy(index + 1), item, sType,
696                         sValue.makeStringAndClear(), sTooltip, pParentEntry, !bLoadAll, bReadOnly,
697                         bWasModified);
698         }
699     }
700 }
701 
getConfigAccess(const OUString & sNodePath,bool bUpdate)702 Reference<XNameAccess> CuiAboutConfigTabPage::getConfigAccess(const OUString& sNodePath,
703                                                               bool bUpdate)
704 {
705     uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
706 
707     uno::Reference<lang::XMultiServiceFactory> xConfigProvider(
708         css::configuration::theDefaultProvider::get(xContext));
709 
710     beans::NamedValue aProperty;
711     aProperty.Name = "nodepath";
712     aProperty.Value <<= sNodePath;
713 
714     uno::Sequence<uno::Any> aArgumentList{ uno::Any(aProperty) };
715 
716     OUString sAccessString;
717 
718     if (bUpdate)
719         sAccessString = "com.sun.star.configuration.ConfigurationUpdateAccess";
720     else
721         sAccessString = "com.sun.star.configuration.ConfigurationAccess";
722 
723     uno::Reference<container::XNameAccess> xNameAccess(
724         xConfigProvider->createInstanceWithArguments(sAccessString, aArgumentList),
725         uno::UNO_QUERY_THROW);
726 
727     return xNameAccess;
728 }
729 
AddToModifiedVector(const std::shared_ptr<Prop_Impl> & rProp)730 void CuiAboutConfigTabPage::AddToModifiedVector(const std::shared_ptr<Prop_Impl>& rProp)
731 {
732     bool isModifiedBefore = false;
733     //Check if value modified before
734     for (std::shared_ptr<Prop_Impl>& nInd : m_vectorOfModified)
735     {
736         if (rProp->Name == nInd->Name && rProp->Property == nInd->Property)
737         {
738             //property modified before. Assign reference to the modified value
739             //do your changes on this object. They will be saved later.
740             nInd = rProp;
741             isModifiedBefore = true;
742             break;
743         }
744     }
745 
746     if (!isModifiedBefore)
747         m_vectorOfModified.push_back(rProp);
748     //property is not modified before
749 }
750 
751 std::vector<OUString>
commaStringToSequence(std::u16string_view rCommaSepString)752 CuiAboutConfigTabPage::commaStringToSequence(std::u16string_view rCommaSepString)
753 {
754     std::vector<OUString> tempVector;
755 
756     sal_Int32 index = 0;
757     do
758     {
759         OUString word(o3tl::getToken(rCommaSepString, 0, u',', index));
760         word = word.trim();
761         if (!word.isEmpty())
762             tempVector.push_back(word);
763     } while (index >= 0);
764     return tempVector;
765 }
766 
IMPL_LINK_NOARG(CuiAboutConfigTabPage,ResetBtnHdl_Impl,weld::Button &,void)767 IMPL_LINK_NOARG(CuiAboutConfigTabPage, ResetBtnHdl_Impl, weld::Button&, void) { Reset(); }
768 
IMPL_LINK_NOARG(CuiAboutConfigTabPage,DoubleClickHdl_Impl,weld::TreeView &,bool)769 IMPL_LINK_NOARG(CuiAboutConfigTabPage, DoubleClickHdl_Impl, weld::TreeView&, bool)
770 {
771     StandardHdl_Impl(*m_xEditBtn);
772     return true;
773 }
774 
IMPL_LINK_NOARG(CuiAboutConfigTabPage,StandardHdl_Impl,weld::Button &,void)775 IMPL_LINK_NOARG(CuiAboutConfigTabPage, StandardHdl_Impl, weld::Button&, void)
776 {
777     if (!m_xPrefBox->get_selected(m_xScratchIter.get()))
778         return;
779 
780     UserData* pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(*m_xScratchIter));
781     if (!pUserData || !pUserData->bIsPropertyPath || pUserData->bIsReadOnly)
782         return;
783 
784     //if selection is a node
785     OUString sPropertyName = m_xPrefBox->get_text(*m_xScratchIter, 1);
786     OUString sPropertyType = m_xPrefBox->get_text(*m_xScratchIter, 2);
787     OUString sPropertyValue = m_xPrefBox->get_text(*m_xScratchIter, 3);
788 
789     auto pProperty
790         = std::make_shared<Prop_Impl>(pUserData->sPropertyPath, sPropertyName, Any(sPropertyValue));
791     bool bSaveChanges = false;
792 
793     bool bOpenDialog = true;
794     OUString sDialogValue;
795 
796     if (sPropertyType == "boolean")
797     {
798         bool bValue;
799         if (sPropertyValue == "true")
800         {
801             sDialogValue = "false";
802             bValue = false;
803         }
804         else
805         {
806             sDialogValue = "true";
807             bValue = true;
808         }
809 
810         pProperty->Value <<= bValue;
811         bOpenDialog = false;
812         bSaveChanges = true;
813     }
814     else
815     {
816         sDialogValue = sPropertyValue;
817         bOpenDialog = true;
818     }
819 
820     try
821     {
822         if (bOpenDialog)
823         {
824             if (sPropertyType == "short" || sPropertyType == "int" || sPropertyType == "long")
825             {
826                 sal_Int64 nMin = sPropertyType == "short"
827                                      ? SAL_MIN_INT16
828                                      : sPropertyType == "int" ? SAL_MIN_INT32 : SAL_MIN_INT64;
829                 sal_Int64 nMax = sPropertyType == "short"
830                                      ? SAL_MAX_INT16
831                                      : sPropertyType == "int" ? SAL_MAX_INT32 : SAL_MAX_INT64;
832                 SvxNumberDialog aNumberDialog(m_xDialog.get(), sPropertyName,
833                                               sDialogValue.toInt64(), nMin, nMax);
834                 if (aNumberDialog.run() == RET_OK)
835                 {
836                     sal_Int64 nNewValue = aNumberDialog.GetNumber();
837                     if (sPropertyType == "short")
838                     {
839                         pProperty->Value <<= static_cast<sal_Int16>(nNewValue);
840                     }
841                     else if (sPropertyType == "int")
842                     {
843                         pProperty->Value <<= static_cast<sal_Int32>(nNewValue);
844                     }
845                     else if (sPropertyType == "long")
846                     {
847                         pProperty->Value <<= nNewValue;
848                     }
849                     bSaveChanges = true;
850                     sDialogValue = OUString::number(nNewValue);
851                 }
852             }
853             else if (sPropertyType == "double")
854             {
855                 SvxDecimalNumberDialog aNumberDialog(m_xDialog.get(), sPropertyName,
856                                                      sDialogValue.toDouble());
857                 if (aNumberDialog.run() == RET_OK)
858                 {
859                     double fNewValue = aNumberDialog.GetNumber();
860                     pProperty->Value <<= fNewValue;
861                     bSaveChanges = true;
862                     sDialogValue = OUString::number(fNewValue);
863                 }
864             }
865             else if (sPropertyType == "string")
866             {
867                 SvxNameDialog aNameDialog(m_xDialog.get(), sDialogValue, sPropertyName);
868                 aNameDialog.SetCheckNameHdl(LINK(this, CuiAboutConfigTabPage, ValidNameHdl));
869                 if (aNameDialog.run() == RET_OK)
870                 {
871                     sDialogValue = aNameDialog.GetName();
872                     pProperty->Value <<= sDialogValue;
873                     bSaveChanges = true;
874                 }
875             }
876             else if (sPropertyType == "short-list")
877             {
878                 SvxListDialog aListDialog(m_xDialog.get());
879                 aListDialog.SetEntries(commaStringToSequence(sDialogValue));
880                 aListDialog.SetMode(ListMode::Int16);
881                 if (aListDialog.run() == RET_OK)
882                 {
883                     std::vector<OUString> seqStr = aListDialog.GetEntries();
884                     uno::Sequence<sal_Int16> seqShort(seqStr.size());
885                     std::transform(
886                         seqStr.begin(), seqStr.end(), seqShort.getArray(),
887                         [](const auto& str) { return static_cast<sal_Int16>(str.toInt32()); });
888                     pProperty->Value <<= seqShort;
889                     sDialogValue = lcl_IntListToString(seqShort);
890                     bSaveChanges = true;
891                 }
892             }
893             else if (sPropertyType == "int-list")
894             {
895                 SvxListDialog aListDialog(m_xDialog.get());
896                 aListDialog.SetEntries(commaStringToSequence(sDialogValue));
897                 aListDialog.SetMode(ListMode::Int32);
898                 if (aListDialog.run() == RET_OK)
899                 {
900                     std::vector<OUString> seqStr = aListDialog.GetEntries();
901                     uno::Sequence<sal_Int32> seq(seqStr.size());
902                     std::transform(
903                         seqStr.begin(), seqStr.end(), seq.getArray(),
904                         [](const auto& str) { return static_cast<sal_Int32>(str.toInt32()); });
905                     pProperty->Value <<= seq;
906                     sDialogValue = lcl_IntListToString(seq);
907                     bSaveChanges = true;
908                 }
909             }
910             else if (sPropertyType == "long-list")
911             {
912                 SvxListDialog aListDialog(m_xDialog.get());
913                 aListDialog.SetEntries(commaStringToSequence(sDialogValue));
914                 aListDialog.SetMode(ListMode::Int64);
915                 if (aListDialog.run() == RET_OK)
916                 {
917                     std::vector<OUString> seqStr = aListDialog.GetEntries();
918                     uno::Sequence<sal_Int64> seq(seqStr.size());
919                     std::transform(
920                         seqStr.begin(), seqStr.end(), seq.getArray(),
921                         [](const auto& str) { return static_cast<sal_Int64>(str.toInt32()); });
922                     pProperty->Value <<= seq;
923                     sDialogValue = lcl_IntListToString(seq);
924                     bSaveChanges = true;
925                 }
926             }
927             else if (sPropertyType == "double-list")
928             {
929                 SvxListDialog aListDialog(m_xDialog.get());
930                 aListDialog.SetEntries(commaStringToSequence(sDialogValue));
931                 aListDialog.SetMode(ListMode::Double);
932                 if (aListDialog.run() == RET_OK)
933                 {
934                     std::vector<OUString> seqStr = aListDialog.GetEntries();
935                     uno::Sequence<double> seq(seqStr.size());
936                     std::transform(
937                         seqStr.begin(), seqStr.end(), seq.getArray(),
938                         [](const auto& str) { return static_cast<double>(str.toDouble()); });
939                     pProperty->Value <<= seq;
940                     sDialogValue = lcl_DoubleListToString(seq);
941                     bSaveChanges = true;
942                 }
943             }
944             else if (sPropertyType == "string-list")
945             {
946                 SvxListDialog aListDialog(m_xDialog.get());
947                 uno::Sequence<OUString> aList
948                     = pUserData->aPropertyValue.get<uno::Sequence<OUString>>();
949                 aListDialog.SetEntries(
950                     comphelper::sequenceToContainer<std::vector<OUString>>(aList));
951                 aListDialog.SetMode(ListMode::String);
952                 if (aListDialog.run() == RET_OK)
953                 {
954                     auto seq = comphelper::containerToSequence(aListDialog.GetEntries());
955                     sDialogValue = lcl_StringListToString(seq);
956                     pProperty->Value <<= seq;
957                     bSaveChanges = true;
958                 }
959             }
960             else //unknown
961                 throw uno::Exception("unknown property type " + sPropertyType, nullptr);
962         }
963 
964         if (bSaveChanges)
965         {
966             AddToModifiedVector(pProperty);
967             pUserData->aPropertyValue = pProperty->Value;
968 
969             //update listbox value.
970             m_xPrefBox->set_text(*m_xScratchIter, sPropertyType, 2);
971             m_xPrefBox->set_text(*m_xScratchIter, sDialogValue, 3);
972             m_xPrefBox->set_text_emphasis(*m_xScratchIter, true, -1);
973             //update m_prefBoxEntries
974             auto it = std::find_if(
975                 m_prefBoxEntries.begin(), m_prefBoxEntries.end(),
976                 [&pUserData, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
977                     return rEntry.pUserData->sPropertyPath == pUserData->sPropertyPath
978                            && rEntry.sStatus == sPropertyName;
979                 });
980             if (it != m_prefBoxEntries.end())
981             {
982                 it->sValue = sDialogValue;
983                 it->pUserData->bWasModified = true;
984 
985                 auto modifiedIt = std::find_if(
986                     m_modifiedPrefBoxEntries.begin(), m_modifiedPrefBoxEntries.end(),
987                     [&pUserData, &sPropertyName](const prefBoxEntry& rEntry) -> bool {
988                         return rEntry.pUserData->sPropertyPath == pUserData->sPropertyPath
989                                && rEntry.sStatus == sPropertyName;
990                     });
991 
992                 if (modifiedIt != m_modifiedPrefBoxEntries.end())
993                 {
994                     modifiedIt->sValue = sDialogValue;
995                     modifiedIt->pUserData->bWasModified = true;
996                 }
997                 else
998                 {
999                     m_modifiedPrefBoxEntries.push_back(*it);
1000                 }
1001             }
1002         }
1003     }
1004     catch (uno::Exception&)
1005     {
1006     }
1007 }
1008 
IMPL_LINK_NOARG(CuiAboutConfigTabPage,SearchHdl_Impl,weld::Button &,void)1009 IMPL_LINK_NOARG(CuiAboutConfigTabPage, SearchHdl_Impl, weld::Button&, void) { InputChanged(); }
1010 
IMPL_LINK_NOARG(CuiAboutConfigTabPage,ModifiedHdl_Impl,weld::Toggleable &,void)1011 IMPL_LINK_NOARG(CuiAboutConfigTabPage, ModifiedHdl_Impl, weld::Toggleable&, void)
1012 {
1013     InputChanged();
1014 }
1015 
InsertEntry(const prefBoxEntry & rEntry)1016 void CuiAboutConfigTabPage::InsertEntry(const prefBoxEntry& rEntry)
1017 {
1018     bool bOnlyModified = m_xModifiedCheckBtn->get_active();
1019     if (bOnlyModified && !rEntry.pUserData->bWasModified)
1020         return;
1021 
1022     OUString sPathWithProperty = rEntry.pUserData->sPropertyPath;
1023     sal_Int32 index = sPathWithProperty.lastIndexOf(rEntry.sProp);
1024     OUString sPath = sPathWithProperty.copy(0, index);
1025     index = 0;
1026     std::unique_ptr<weld::TreeIter> xParentEntry(m_xPrefBox->make_iterator());
1027     std::unique_ptr<weld::TreeIter> xGrandParentEntry;
1028 
1029     do
1030     {
1031         int prevIndex = index;
1032         index = sPath.indexOf("/", index + 1);
1033         // deal with no parent case (tdf#107811)
1034         if (index < 0)
1035         {
1036             OUString sId(weld::toId(rEntry.pUserData));
1037             m_xPrefBox->insert(nullptr, -1, &rEntry.sProp, &sId, nullptr, nullptr, false,
1038                                m_xScratchIter.get());
1039             m_xPrefBox->set_text(*m_xScratchIter, rEntry.sStatus, 1);
1040             m_xPrefBox->set_text(*m_xScratchIter, rEntry.sType, 2);
1041             m_xPrefBox->set_text(*m_xScratchIter, rEntry.sValue, 3);
1042             m_xPrefBox->set_text_emphasis(*m_xScratchIter, rEntry.pUserData->bWasModified, -1);
1043             m_xPrefBox->set_sensitive(*m_xScratchIter, !rEntry.pUserData->bIsReadOnly);
1044             return;
1045         }
1046         OUString sParentName = sPath.copy(prevIndex + 1, index - prevIndex - 1);
1047 
1048         bool hasEntry = false;
1049         bool bStartOk;
1050 
1051         if (!xGrandParentEntry)
1052             bStartOk = m_xPrefBox->get_iter_first(*xParentEntry);
1053         else
1054         {
1055             m_xPrefBox->copy_iterator(*xGrandParentEntry, *xParentEntry);
1056             bStartOk = m_xPrefBox->iter_children(*xParentEntry);
1057         }
1058 
1059         if (bStartOk)
1060         {
1061             do
1062             {
1063                 if (m_xPrefBox->get_text(*xParentEntry, 0) == sParentName)
1064                 {
1065                     hasEntry = true;
1066                     break;
1067                 }
1068             } while (m_xPrefBox->iter_next_sibling(*xParentEntry));
1069         }
1070 
1071         if (!hasEntry)
1072         {
1073             m_xPrefBox->insert(xGrandParentEntry.get(), -1, &sParentName, nullptr, nullptr, nullptr,
1074                                false, xParentEntry.get());
1075             //It is needed, without this the selection line will be truncated.
1076             m_xPrefBox->set_text(*xParentEntry, u""_ustr, 1);
1077             m_xPrefBox->set_text(*xParentEntry, u""_ustr, 2);
1078             m_xPrefBox->set_text(*xParentEntry, u""_ustr, 3);
1079             m_xPrefBox->set_sensitive(*xParentEntry, true);
1080         }
1081 
1082         xGrandParentEntry = m_xPrefBox->make_iterator(xParentEntry.get());
1083     } while (index < sPath.getLength() - 1);
1084 
1085     OUString sId(weld::toId(rEntry.pUserData));
1086     m_xPrefBox->insert(xParentEntry.get(), -1, &rEntry.sProp, &sId, nullptr, nullptr, false,
1087                        m_xScratchIter.get());
1088     m_xPrefBox->set_text(*m_xScratchIter, rEntry.sStatus, 1);
1089     m_xPrefBox->set_text(*m_xScratchIter, rEntry.sType, 2);
1090     m_xPrefBox->set_text(*m_xScratchIter, rEntry.sValue, 3);
1091     m_xPrefBox->set_text_emphasis(*m_xScratchIter, rEntry.pUserData->bWasModified, -1);
1092     m_xPrefBox->set_sensitive(*m_xScratchIter, !rEntry.pUserData->bIsReadOnly);
1093 }
1094 
IMPL_LINK(CuiAboutConfigTabPage,ExpandingHdl_Impl,const weld::TreeIter &,rEntry,bool)1095 IMPL_LINK(CuiAboutConfigTabPage, ExpandingHdl_Impl, const weld::TreeIter&, rEntry, bool)
1096 {
1097     if (m_xPrefBox->iter_has_child(rEntry))
1098         return true;
1099     UserData* pUserData = weld::fromId<UserData*>(m_xPrefBox->get_id(rEntry));
1100     if (pUserData && !pUserData->bIsPropertyPath)
1101     {
1102         assert(pUserData->aXNameAccess.is());
1103         FillItems(pUserData->aXNameAccess, &rEntry, pUserData->aLineage);
1104     }
1105     return true;
1106 }
1107 
1108 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1109