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