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 "WriterInspectorTextPanel.hxx"
21 
22 #include <doc.hxx>
23 #include <ndtxt.hxx>
24 #include <docsh.hxx>
25 #include <wrtsh.hxx>
26 #include <unoprnms.hxx>
27 #include <editeng/unoprnms.hxx>
28 #include <com/sun/star/text/XBookmarksSupplier.hpp>
29 #include <com/sun/star/text/XTextRange.hpp>
30 #include <com/sun/star/text/XTextRangeCompare.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/beans/XPropertyState.hpp>
33 #include <com/sun/star/frame/XFrame.hpp>
34 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
35 #include <com/sun/star/table/BorderLine2.hpp>
36 #include <com/sun/star/lang/IllegalArgumentException.hpp>
37 #include <com/sun/star/rdf/XMetadatable.hpp>
38 #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
39 
40 #include <unotextrange.hxx>
41 #include <comphelper/string.hxx>
42 #include <comphelper/processfactory.hxx>
43 #include <i18nlangtag/languagetag.hxx>
44 #include <vcl/settings.hxx>
45 #include <inspectorproperties.hrc>
46 #include <strings.hrc>
47 #include <rdfhelper.hxx>
48 
49 namespace sw::sidebar
50 {
51 static void UpdateTree(SwDocShell* pDocSh, std::vector<svx::sidebar::TreeNode>& aStore);
52 
Create(weld::Widget * pParent)53 std::unique_ptr<PanelLayout> WriterInspectorTextPanel::Create(weld::Widget* pParent)
54 {
55     if (pParent == nullptr)
56         throw lang::IllegalArgumentException(
57             "no parent Window given to WriterInspectorTextPanel::Create", nullptr, 0);
58     return std::make_unique<WriterInspectorTextPanel>(pParent);
59 }
60 
WriterInspectorTextPanel(weld::Widget * pParent)61 WriterInspectorTextPanel::WriterInspectorTextPanel(weld::Widget* pParent)
62     : InspectorTextPanel(pParent)
63 {
64     SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current());
65     m_pShell = pDocSh ? pDocSh->GetWrtShell() : nullptr;
66     if (m_pShell)
67     {
68         m_oldLink = m_pShell->GetChgLnk();
69         m_pShell->SetChgLnk(LINK(this, WriterInspectorTextPanel, AttrChangedNotify));
70     }
71 
72     // Update panel on start
73     std::vector<svx::sidebar::TreeNode> aStore;
74     if (pDocSh && pDocSh->GetDoc()->GetEditShell()->GetCursor()->GetNode().GetTextNode())
75         UpdateTree(pDocSh, aStore);
76     updateEntries(aStore);
77 }
78 
~WriterInspectorTextPanel()79 WriterInspectorTextPanel::~WriterInspectorTextPanel() { m_pShell->SetChgLnk(m_oldLink); }
80 
PropertyNametoRID(const OUString & rName)81 static OUString PropertyNametoRID(const OUString& rName)
82 {
83     static const std::map<OUString, const char*> aNameToRID = {
84         { "BorderDistance", RID_BORDER_DISTANCE },
85         { "BottomBorder", RID_BOTTOM_BORDER },
86         { "BottomBorderDistance", RID_BOTTOM_BORDER_DISTANCE },
87         { "BreakType", RID_BREAK_TYPE },
88         { "Category", RID_CATEGORY },
89         { "Cell", RID_CELL },
90         { "CharAutoEscapement", RID_CHAR_AUTO_ESCAPEMENT },
91         { "CharAutoKerning", RID_CHAR_AUTO_KERNING },
92         { "CharAutoStyleName", RID_CHAR_AUTO_STYLE_NAME },
93         { "CharBackColor", RID_CHAR_BACK_COLOR },
94         { "CharBackTransparent", RID_CHAR_BACK_TRANSPARENT },
95         { "CharBorderDistance", RID_CHAR_BORDER_DISTANCE },
96         { "CharBottomBorder", RID_CHAR_BOTTOM_BORDER },
97         { "CharBottomBorderDistance", RID_CHAR_BOTTOM_BORDER_DISTANCE },
98         { "CharCaseMap", RID_CHAR_CASE_MAP },
99         { "CharColor", RID_CHAR_COLOR },
100         { "CharCombineIsOn", RID_CHAR_COMBINE_IS_ON },
101         { "CharCombinePrefix", RID_CHAR_COMBINE_PREFIX },
102         { "CharCombineSuffix", RID_CHAR_COMBINE_SUFFIX },
103         { "CharContoured", RID_CHAR_CONTOURED },
104         { "CharCrossedOut", RID_CHAR_CROSSED_OUT },
105         { "CharDiffHeight", RID_CHAR_DIFF_HEIGHT },
106         { "CharDiffHeightAsian", RID_CHAR_DIFF_HEIGHT_ASIAN },
107         { "CharDiffHeightComplex", RID_CHAR_DIFF_HEIGHT_COMPLEX },
108         { "CharEmphasis", RID_CHAR_EMPHASIS },
109         { "CharEscapement", RID_CHAR_ESCAPEMENT },
110         { "CharEscapementHeight", RID_CHAR_ESCAPEMENT_HEIGHT },
111         { "CharFlash", RID_CHAR_FLASH },
112         { "CharFontCharSet", RID_CHAR_FONT_CHAR_SET },
113         { "CharFontCharSetAsian", RID_CHAR_FONT_CHAR_SET_ASIAN },
114         { "CharFontCharSetComplex", RID_CHAR_FONT_CHAR_SET_COMPLEX },
115         { "CharFontFamily", RID_CHAR_FONT_FAMILY },
116         { "CharFontFamilyAsian", RID_CHAR_FONT_FAMILY_ASIAN },
117         { "CharFontFamilyComplex", RID_CHAR_FONT_FAMILY_COMPLEX },
118         { "CharFontName", RID_CHAR_FONT_NAME },
119         { "CharFontNameAsian", RID_CHAR_FONT_NAME_ASIAN },
120         { "CharFontNameComplex", RID_CHAR_FONT_NAME_COMPLEX },
121         { "CharFontPitch", RID_CHAR_FONT_PITCH },
122         { "CharFontPitchAsian", RID_CHAR_FONT_PITCH_ASIAN },
123         { "CharFontPitchComplex", RID_CHAR_FONT_PITCH_COMPLEX },
124         { "CharFontStyleName", RID_CHAR_FONT_STYLE_NAME },
125         { "CharFontStyleNameAsian", RID_CHAR_FONT_STYLE_NAME_ASIAN },
126         { "CharFontStyleNameComplex", RID_CHAR_FONT_STYLE_NAME_COMPLEX },
127         { "CharHeight", RID_CHAR_HEIGHT },
128         { "CharHeightAsian", RID_CHAR_HEIGHT_ASIAN },
129         { "CharHeightComplex", RID_CHAR_HEIGHT_COMPLEX },
130         { "CharHidden", RID_CHAR_HIDDEN },
131         { "CharHighlight", RID_CHAR_HIGHLIGHT },
132         { "CharInteropGrabBag", RID_CHAR_INTEROP_GRAB_BAG },
133         { "CharKerning", RID_CHAR_KERNING },
134         { "CharLeftBorder", RID_CHAR_LEFT_BORDER },
135         { "CharLeftBorderDistance", RID_CHAR_LEFT_BORDER_DISTANCE },
136         { "CharLocale", RID_CHAR_LOCALE },
137         { "CharLocaleAsian", RID_CHAR_LOCALE_ASIAN },
138         { "CharLocaleComplex", RID_CHAR_LOCALE_COMPLEX },
139         { "CharNoHyphenation", RID_CHAR_NO_HYPHENATION },
140         { "CharOverline", RID_CHAR_OVERLINE },
141         { "CharOverlineColor", RID_CHAR_OVERLINE_COLOR },
142         { "CharOverlineHasColor", RID_CHAR_OVERLINE_HAS_COLOR },
143         { "CharPosture", RID_CHAR_POSTURE },
144         { "CharPostureAsian", RID_CHAR_POSTURE_ASIAN },
145         { "CharPostureComplex", RID_CHAR_POSTURE_COMPLEX },
146         { "CharPropHeight", RID_CHAR_PROP_HEIGHT },
147         { "CharPropHeightAsian", RID_CHAR_PROP_HEIGHT_ASIAN },
148         { "CharPropHeightComplex", RID_CHAR_PROP_HEIGHT_COMPLEX },
149         { "CharRelief", RID_CHAR_RELIEF },
150         { "CharRightBorder", RID_CHAR_RIGHT_BORDER },
151         { "CharRightBorderDistance", RID_CHAR_RIGHT_BORDER_DISTANCE },
152         { "CharRotation", RID_CHAR_ROTATION },
153         { "CharRotationIsFitToLine", RID_CHAR_ROTATION_IS_FIT_TO_LINE },
154         { "CharScaleWidth", RID_CHAR_SCALE_WIDTH },
155         { "CharShadingValue", RID_CHAR_SHADING_VALUE },
156         { "CharShadowFormat", RID_CHAR_SHADOW_FORMAT },
157         { "CharShadowed", RID_CHAR_SHADOWED },
158         { "CharStrikeout", RID_CHAR_STRIKEOUT },
159         { "CharStyleName", RID_CHAR_STYLE_NAME },
160         { "CharStyleNames", RID_CHAR_STYLE_NAMES },
161         { "CharTopBorder", RID_CHAR_TOP_BORDER },
162         { "CharTopBorderDistance", RID_CHAR_TOP_BORDER_DISTANCE },
163         { "CharTransparence", RID_CHAR_TRANSPARENCE },
164         { "CharUnderline", RID_CHAR_UNDERLINE },
165         { "CharUnderlineColor", RID_CHAR_UNDERLINE_COLOR },
166         { "CharUnderlineHasColor", RID_CHAR_UNDERLINE_HAS_COLOR },
167         { "CharWeight", RID_CHAR_WEIGHT },
168         { "CharWeightAsian", RID_CHAR_WEIGHT_ASIAN },
169         { "CharWeightComplex", RID_CHAR_WEIGHT_COMPLEX },
170         { "CharWordMode", RID_CHAR_WORD_MODE },
171         { "ContinueingPreviousSubTree", RID_CONTINUING_PREVIOUS_SUB_TREE },
172         { "DisplayName", RID_DISPLAY_NAME },
173         { "DocumentIndex", RID_DOCUMENT_INDEX },
174         { "DocumentIndexMark", RID_DOCUMENT_INDEX_MARK },
175         { "DropCapCharStyleName", RID_DROP_CAP_CHAR_STYLE_NAME },
176         { "DropCapFormat", RID_DROP_CAP_FORMAT },
177         { "DropCapWholeWord", RID_DROP_CAP_WHOLE_WORD },
178         { "Endnote", RID_ENDNOTE },
179         { "FillBackground", RID_FILL_BACKGROUND },
180         { "FillBitmap", RID_FILL_BITMAP },
181         { "FillBitmapLogicalSize", RID_FILL_BITMAP_LOGICAL_SIZE },
182         { "FillBitmapMode", RID_FILL_BITMAP_MODE },
183         { "FillBitmapName", RID_FILL_BITMAP_NAME },
184         { "FillBitmapOffsetX", RID_FILL_BITMAP_OFFSET_X },
185         { "FillBitmapOffsetY", RID_FILL_BITMAP_OFFSET_Y },
186         { "FillBitmapPositionOffsetX", RID_FILL_BITMAP_POSITION_OFFSET_X },
187         { "FillBitmapPositionOffsetY", RID_FILL_BITMAP_POSITION_OFFSET_Y },
188         { "FillBitmapRectanglePoint", RID_FILL_BITMAP_RECTANGLE_POINT },
189         { "FillBitmapSizeX", RID_FILL_BITMAP_SIZE_X },
190         { "FillBitmapSizeY", RID_FILL_BITMAP_SIZE_Y },
191         { "FillBitmapStretch", RID_FILL_BITMAP_STRETCH },
192         { "FillBitmapTile", RID_FILL_BITMAP_TILE },
193         { "FillBitmapURL", RID_FILL_BITMAP_URL },
194         { "FillColor", RID_FILL_COLOR },
195         { "FillColor2", RID_FILL_COLOR2 },
196         { "FillGradient", RID_FILL_GRADIENT },
197         { "FillGradientName", RID_FILL_GRADIENT_NAME },
198         { "FillGradientStepCount", RID_FILL_GRADIENT_STEP_COUNT },
199         { "FillHatch", RID_FILL_HATCH },
200         { "FillHatchName", RID_FILL_HATCH_NAME },
201         { "FillStyle", RID_FILL_STYLE },
202         { "FillTransparence", RID_FILL_TRANSPARENCE },
203         { "FillTransparenceGradient", RID_FILL_TRANSPARENCE_GRADIENT },
204         { "FillTransparenceGradientName", RID_FILL_TRANSPARENCE_GRADIENT_NAME },
205         { "FollowStyle", RID_FOLLOW_STYLE },
206         { "Footnote", RID_FOOTNOTE },
207         { "Hidden", RID_HIDDEN },
208         { "HyperLinkEvents", RID_HYPERLINK_EVENTS },
209         { "HyperLinkName", RID_HYPERLINK_NAME },
210         { "HyperLinkTarget", RID_HYPERLINK_TARGET },
211         { "HyperLinkURL", RID_HYPERLINK_URL },
212         { "IsAutoUpdate", RID_IS_AUTO_UPDATE },
213         { "IsPhysical", RID_IS_PHYSICAL },
214         { "LeftBorder", RID_LEFT_BORDER },
215         { "LeftBorderDistance", RID_LEFT_BORDER_DISTANCE },
216         { "ListAutoFormat", RID_LIST_AUTO_FORMAT },
217         { "ListId", RID_LIST_ID },
218         { "ListLabelString", RID_LIST_LABEL_STRING },
219         { "MetadataReference", RID_METADATA_REFERENCE },
220         { "NestedTextContent", RID_NESTED_TEXT_CONTENT },
221         { "NumberingIsNumber", RID_NUMBERING_IS_NUMBER },
222         { "NumberingLevel", RID_NUMBERING_LEVEL },
223         { "NumberingRules", RID_NUMBERING_RULES },
224         { "NumberingStartValue", RID_NUMBERING_START_VALUE },
225         { "NumberingStyleName", RID_NUMBERING_STYLE_NAME },
226         { "OutlineContentVisible", RID_OUTLINE_CONTENT_VISIBLE },
227         { "OutlineLevel", RID_OUTLINE_LEVEL },
228         { "PageDescName", RID_PAGE_DESC_NAME },
229         { "PageNumberOffset", RID_PAGE_NUMBER_OFFSET },
230         { "PageStyleName", RID_PAGE_STYLE_NAME },
231         { "ParRsid", RID_PAR_RSID },
232         { "ParaAdjust", RID_PARA_ADJUST },
233         { "ParaAutoStyleName", RID_PARA_AUTO_STYLE_NAME },
234         { "ParaBackColor", RID_PARA_BACK_COLOR },
235         { "ParaBackGraphic", RID_PARA_BACK_GRAPHIC },
236         { "ParaBackGraphicFilter", RID_PARA_BACK_GRAPHIC_FILTER },
237         { "ParaBackGraphicLocation", RID_PARA_BACK_GRAPHIC_LOCATION },
238         { "ParaBackGraphicURL", RID_PARA_BACK_GRAPHIC_URL },
239         { "ParaBackTransparent", RID_PARA_BACK_TRANSPARENT },
240         { "ParaBottomMargin", RID_PARA_BOTTOM_MARGIN },
241         { "ParaBottomMarginRelative", RID_PARA_BOTTOM_MARGIN_RELATIVE },
242         { "ParaChapterNumberingLevel", RID_PARA_CHAPTER_NUMBERING_LEVEL },
243         { "ParaConditionalStyleName", RID_PARA_CONDITIONAL_STYLE_NAME },
244         { "ParaContextMargin", RID_PARA_CONTEXT_MARGIN },
245         { "ParaExpandSingleWord", RID_PARA_EXPAND_SINGLE_WORD },
246         { "ParaFirstLineIndent", RID_PARA_FIRST_LINE_INDENT },
247         { "ParaFirstLineIndentRelative", RID_PARA_FIRST_LINE_INDENT_RELATIVE },
248         { "ParaHyphenationMaxHyphens", RID_PARA_HYPHENATION_MAX_HYPHENS },
249         { "ParaHyphenationMaxLeadingChars", RID_PARA_HYPHENATION_MAX_LEADING_CHARS },
250         { "ParaHyphenationMaxTrailingChars", RID_PARA_HYPHENATION_MAX_TRAILING_CHARS },
251         { "ParaHyphenationNoCaps", RID_PARA_HYPHENATION_NO_CAPS },
252         { "ParaInteropGrabBag", RID_PARA_INTEROP_GRAB_BAG },
253         { "ParaIsAutoFirstLineIndent", RID_PARA_IS_AUTO_FIRST_LINE_INDENT },
254         { "ParaIsCharacterDistance", RID_PARA_IS_CHARACTER_DISTANCE },
255         { "ParaIsConnectBorder", RID_PARA_IS_CONNECT_BORDER },
256         { "ParaIsForbiddenRules", RID_PARA_IS_FORBIDDEN_RULES },
257         { "ParaIsHangingPunctuation", RID_PARA_IS_HANGING_PUNCTUATION },
258         { "ParaIsHyphenation", RID_PARA_IS_HYPHENATION },
259         { "ParaIsNumberingRestart", RID_PARA_IS_NUMBERING_RESTART },
260         { "ParaKeepTogether", RID_PARA_KEEP_TOGETHER },
261         { "ParaLastLineAdjust", RID_PARA_LAST_LINE_ADJUST },
262         { "ParaLeftMargin", RID_PARA_LEFT_MARGIN },
263         { "ParaLeftMarginRelative", RID_PARA_LEFT_MARGIN_RELATIVE },
264         { "ParaLineNumberCount", RID_PARA_LINE_NUMBER_COUNT },
265         { "ParaLineNumberStartValue", RID_PARA_LINE_NUMBER_START_VALUE },
266         { "ParaLineSpacing", RID_PARA_LINE_SPACING },
267         { "ParaOrphans", RID_PARA_ORPHANS },
268         { "ParaRegisterModeActive", RID_PARA_REGISTER_MODE_ACTIVE },
269         { "ParaRightMargin", RID_PARA_RIGHT_MARGIN },
270         { "ParaRightMarginRelative", RID_PARA_RIGHT_MARGIN_RELATIVE },
271         { "ParaShadowFormat", RID_PARA_SHADOW_FORMAT },
272         { "ParaSplit", RID_PARA_SPLIT },
273         { "ParaStyleName", RID_PARA_STYLE_NAME },
274         { "ParaTabStops", RID_PARA_TAB_STOPS },
275         { "ParaTopMargin", RID_PARA_TOP_MARGIN },
276         { "ParaTopMarginRelative", RID_PARA_TOP_MARGIN_RELATIVE },
277         { "ParaUserDefinedAttributes", RID_PARA_USER_DEFINED_ATTRIBUTES },
278         { "ParaVertAlignment", RID_PARA_VERT_ALIGNMENT },
279         { "ParaWidows", RID_PARA_WIDOWS },
280         { "ReferenceMark", RID_REFERENCE_MARK },
281         { "RightBorder", RID_RIGHT_BORDER },
282         { "RightBorderDistance", RID_RIGHT_BORDER_DISTANCE },
283         { "Rsid", RID_RSID },
284         { "RubyAdjust", RID_RUBY_ADJUST },
285         { "RubyCharStyleName", RID_RUBY_CHAR_STYLE_NAME },
286         { "RubyIsAbove", RID_RUBY_IS_ABOVE },
287         { "RubyPosition", RID_RUBY_POSITION },
288         { "RubyText", RID_RUBY_TEXT },
289         { "SnapToGrid", RID_SNAP_TO_GRID },
290         { "StyleInteropGrabBag", RID_STYLE_INTEROP_GRAB_BAG },
291         { "TextField", RID_TEXT_FIELD },
292         { "TextFrame", RID_TEXT_FRAME },
293         { "TextParagraph", RID_TEXT_PARAGRAPH },
294         { "TextSection", RID_TEXT_SECTION },
295         { "TextTable", RID_TEXT_TABLE },
296         { "TextUserDefinedAttributes", RID_TEXT_USER_DEFINED_ATTRIBUTES },
297         { "TopBorder", RID_TOP_BORDER },
298         { "TopBorderDistance", RID_TOP_BORDER_DISTANCE },
299         { "UnvisitedCharStyleName", RID_UNVISITED_CHAR_STYLE_NAME },
300         { "VisitedCharStyleName", RID_VISITED_CHAR_STYLE_NAME },
301         { "WritingMode", RID_WRITING_MODE },
302         { "BorderColor", RID_BORDER_COLOR },
303         { "BorderInnerLineWidth", RID_BORDER_INNER_LINE_WIDTH },
304         { "BorderLineDistance", RID_BORDER_LINE_DISTANCE },
305         { "BorderLineStyle", RID_BORDER_LINE_STYLE },
306         { "BorderLineWidth", RID_BORDER_LINE_WIDTH },
307         { "BorderOuterLineWidth", RID_BORDER_OUTER_LINE_WIDTH },
308     };
309 
310     auto itr = aNameToRID.find(rName);
311     if (itr != aNameToRID.end())
312         return SwResId(itr->second);
313     return rName;
314 }
315 
SimplePropToTreeNode(const OUString & rName,const css::uno::Any & rVal)316 static svx::sidebar::TreeNode SimplePropToTreeNode(const OUString& rName, const css::uno::Any& rVal)
317 {
318     svx::sidebar::TreeNode aCurNode;
319     aCurNode.sNodeName = PropertyNametoRID(rName);
320     aCurNode.aValue = rVal;
321 
322     return aCurNode;
323 }
324 
BorderToTreeNode(const OUString & rName,const css::uno::Any & rVal)325 static svx::sidebar::TreeNode BorderToTreeNode(const OUString& rName, const css::uno::Any& rVal)
326 {
327     table::BorderLine2 aBorder;
328     rVal >>= aBorder;
329     svx::sidebar::TreeNode aCurNode;
330     aCurNode.sNodeName = PropertyNametoRID(rName);
331     aCurNode.NodeType = svx::sidebar::TreeNode::ComplexProperty;
332 
333     aCurNode.children.push_back(SimplePropToTreeNode("BorderColor", css::uno::Any(aBorder.Color)));
334     aCurNode.children.push_back(
335         SimplePropToTreeNode("BorderLineWidth", css::uno::Any(aBorder.LineWidth)));
336     aCurNode.children.push_back(
337         SimplePropToTreeNode("BorderLineStyle", css::uno::Any(aBorder.LineStyle)));
338     aCurNode.children.push_back(
339         SimplePropToTreeNode("BorderLineDistance", css::uno::Any(aBorder.LineDistance)));
340     aCurNode.children.push_back(
341         SimplePropToTreeNode("BorderInnerLineWidth", css::uno::Any(aBorder.InnerLineWidth)));
342     aCurNode.children.push_back(
343         SimplePropToTreeNode("BorderOuterLineWidth", css::uno::Any(aBorder.OuterLineWidth)));
344 
345     return aCurNode;
346 }
347 
LocaleToTreeNode(const OUString & rName,const css::uno::Any & rVal)348 static svx::sidebar::TreeNode LocaleToTreeNode(const OUString& rName, const css::uno::Any& rVal)
349 {
350     svx::sidebar::TreeNode aCurNode;
351     aCurNode.sNodeName = PropertyNametoRID(rName);
352     lang::Locale aLocale;
353     rVal >>= aLocale;
354     OUString aLocaleText(aLocale.Language + "-" + aLocale.Country);
355     if (!aLocale.Variant.isEmpty())
356         aLocaleText += " (" + aLocale.Variant + ")";
357     aCurNode.aValue <<= aLocaleText;
358 
359     return aCurNode;
360 }
361 
362 // Collect text of the current level of the annotated text
363 // ranges (InContentMetadata) and metadata fields (MetadataField)
NestedTextContentToText(const css::uno::Any & rVal)364 static OUString NestedTextContentToText(const css::uno::Any& rVal)
365 {
366     uno::Reference<container::XEnumerationAccess> xMeta;
367     if (rVal >>= xMeta)
368     {
369         uno::Reference<container::XEnumeration> xMetaPortions = xMeta->createEnumeration();
370 
371         OUStringBuffer aBuf;
372         while (xMetaPortions->hasMoreElements())
373         {
374             uno::Reference<css::text::XTextRange> xRng(xMetaPortions->nextElement(),
375                                                        uno::UNO_QUERY);
376             aBuf.append(xRng->getString());
377         }
378         return aBuf.makeStringAndClear();
379     }
380 
381     return OUString();
382 }
383 
384 // List metadata associated to the paragraph or character range
MetadataToTreeNode(const css::uno::Reference<css::uno::XInterface> & rSource,svx::sidebar::TreeNode & rNode)385 static void MetadataToTreeNode(const css::uno::Reference<css::uno::XInterface>& rSource,
386                                svx::sidebar::TreeNode& rNode)
387 {
388     uno::Reference<rdf::XMetadatable> xMeta(rSource, uno::UNO_QUERY_THROW);
389     // don't add tree node "Metadata Reference", if there is no xml:id
390     if (xMeta.is() && !xMeta->getMetadataReference().Second.isEmpty())
391     {
392         svx::sidebar::TreeNode aCurNode;
393         aCurNode.sNodeName = PropertyNametoRID("MetadataReference");
394         aCurNode.NodeType = svx::sidebar::TreeNode::ComplexProperty;
395 
396         aCurNode.children.push_back(
397             SimplePropToTreeNode("xml:id", uno::makeAny(xMeta->getMetadataReference().Second)));
398 
399         // list associated (predicate, object) pairs of the actual subject
400         // under the tree node "Metadata Reference"
401         SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current());
402         uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(pDocSh->GetBaseModel(),
403                                                                              uno::UNO_QUERY);
404         const uno::Reference<rdf::XRepository>& xRepo = xDocumentMetadataAccess->getRDFRepository();
405         const css::uno::Reference<css::rdf::XResource> xSubject(rSource, uno::UNO_QUERY);
406         std::map<OUString, OUString> xStatements
407             = SwRDFHelper::getStatements(pDocSh->GetBaseModel(), xRepo->getGraphNames(), xSubject);
408         for (const auto& pair : xStatements)
409             aCurNode.children.push_back(
410                 SimplePropToTreeNode(pair.first, uno::makeAny(pair.second)));
411 
412         rNode.children.push_back(aCurNode);
413     }
414 }
415 
416 static svx::sidebar::TreeNode
PropertyToTreeNode(const css::beans::Property & rProperty,const uno::Reference<beans::XPropertySet> & xPropertiesSet,const bool rIsGrey)417 PropertyToTreeNode(const css::beans::Property& rProperty,
418                    const uno::Reference<beans::XPropertySet>& xPropertiesSet, const bool rIsGrey)
419 {
420     const OUString& rPropName = rProperty.Name;
421     svx::sidebar::TreeNode aCurNode;
422     const uno::Any aAny = xPropertiesSet->getPropertyValue(rPropName);
423     aCurNode.sNodeName = PropertyNametoRID(rPropName);
424 
425     // These properties are handled separately as they are stored in STRUCT and not in single data members
426     if (rPropName == "CharTopBorder" || rPropName == "CharBottomBorder"
427         || rPropName == "CharLeftBorder" || rPropName == "CharRightBorder"
428         || rPropName == "TopBorder" || rPropName == "BottomBorder" || rPropName == "LeftBorder"
429         || rPropName == "RightBorder")
430     {
431         aCurNode = BorderToTreeNode(rPropName, aAny);
432     }
433     else if (rPropName == "CharLocale")
434     {
435         aCurNode = LocaleToTreeNode(rPropName, aAny);
436     }
437     else
438         aCurNode = SimplePropToTreeNode(rPropName, aAny);
439 
440     if (rIsGrey)
441     {
442         aCurNode.isGrey = true;
443         for (svx::sidebar::TreeNode& rChildNode : aCurNode.children)
444             rChildNode.isGrey = true; // grey out all the children nodes
445     }
446 
447     return aCurNode;
448 }
449 
InsertValues(const css::uno::Reference<css::uno::XInterface> & rSource,std::unordered_map<OUString,bool> & rIsDefined,svx::sidebar::TreeNode & rNode,const bool isRoot,const std::vector<OUString> & rHiddenProperty)450 static void InsertValues(const css::uno::Reference<css::uno::XInterface>& rSource,
451                          std::unordered_map<OUString, bool>& rIsDefined,
452                          svx::sidebar::TreeNode& rNode, const bool isRoot,
453                          const std::vector<OUString>& rHiddenProperty)
454 {
455     uno::Reference<beans::XPropertySet> xPropertiesSet(rSource, uno::UNO_QUERY_THROW);
456     uno::Reference<beans::XPropertyState> xPropertiesState(rSource, uno::UNO_QUERY_THROW);
457     const uno::Sequence<beans::Property> aProperties
458         = xPropertiesSet->getPropertySetInfo()->getProperties();
459 
460     for (const beans::Property& rProperty : aProperties)
461     {
462         const OUString& rPropName = rProperty.Name;
463         if (std::find(rHiddenProperty.begin(), rHiddenProperty.end(), rPropName)
464             != rHiddenProperty.end())
465             continue;
466 
467         if (isRoot
468             || xPropertiesState->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
469         {
470             svx::sidebar::TreeNode aCurNode
471                 = PropertyToTreeNode(rProperty, xPropertiesSet, rIsDefined[rPropName]);
472             rIsDefined[rPropName] = true;
473 
474             // process NestedTextContent and show associated metadata
475             // under the tree node "Metadata Reference", if they exist
476             if (rPropName == "NestedTextContent")
477             {
478                 uno::Reference<container::XEnumerationAccess> xMeta;
479                 if (aCurNode.aValue >>= xMeta)
480                     MetadataToTreeNode(xMeta, rNode);
481                 aCurNode.aValue <<= NestedTextContentToText(aCurNode.aValue);
482             }
483 
484             rNode.children.push_back(aCurNode);
485         }
486     }
487 
488     const comphelper::string::NaturalStringSorter aSorter(
489         comphelper::getProcessComponentContext(),
490         Application::GetSettings().GetUILanguageTag().getLocale());
491 
492     std::sort(
493         rNode.children.begin(), rNode.children.end(),
494         [&aSorter](svx::sidebar::TreeNode const& rEntry1, svx::sidebar::TreeNode const& rEntry2) {
495             return aSorter.compare(rEntry1.sNodeName, rEntry2.sNodeName) < 0;
496         });
497 }
498 
UpdateTree(SwDocShell * pDocSh,std::vector<svx::sidebar::TreeNode> & aStore)499 static void UpdateTree(SwDocShell* pDocSh, std::vector<svx::sidebar::TreeNode>& aStore)
500 {
501     SwDoc* pDoc = pDocSh->GetDoc();
502     SwPaM* pCursor = pDoc->GetEditShell()->GetCursor();
503     svx::sidebar::TreeNode aCharDFNode;
504     svx::sidebar::TreeNode aCharNode;
505     svx::sidebar::TreeNode aParaNode;
506     svx::sidebar::TreeNode aParaDFNode;
507     svx::sidebar::TreeNode aBookmarksNode;
508 
509     aCharNode.sNodeName = SwResId(STR_CHARACTERSTYLEFAMILY);
510     aParaNode.sNodeName = SwResId(STR_PARAGRAPHSTYLEFAMILY);
511     aCharDFNode.sNodeName = SwResId(RID_CHAR_DIRECTFORMAT);
512     aParaDFNode.sNodeName = SwResId(RID_PARA_DIRECTFORMAT);
513     aBookmarksNode.sNodeName = SwResId(STR_CONTENT_TYPE_BOOKMARK);
514     aCharDFNode.NodeType = svx::sidebar::TreeNode::Category;
515     aCharNode.NodeType = svx::sidebar::TreeNode::Category;
516     aParaNode.NodeType = svx::sidebar::TreeNode::Category;
517     aParaDFNode.NodeType = svx::sidebar::TreeNode::Category;
518     aBookmarksNode.NodeType = svx::sidebar::TreeNode::Category;
519 
520     uno::Reference<text::XTextRange> xRange(
521         SwXTextRange::CreateXTextRange(*pDoc, *pCursor->GetPoint(), nullptr));
522     uno::Reference<beans::XPropertySet> xPropertiesSet(xRange, uno::UNO_QUERY_THROW);
523     std::unordered_map<OUString, bool> aIsDefined;
524 
525     const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
526                                                    UNO_NAME_PARA_IS_NUMBERING_RESTART,
527                                                    UNO_NAME_PARA_STYLE_NAME,
528                                                    UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
529                                                    UNO_NAME_PAGE_STYLE_NAME,
530                                                    UNO_NAME_NUMBERING_START_VALUE,
531                                                    UNO_NAME_NUMBERING_IS_NUMBER,
532                                                    UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
533                                                    UNO_NAME_CHAR_STYLE_NAME,
534                                                    UNO_NAME_NUMBERING_LEVEL,
535                                                    UNO_NAME_PARRSID };
536 
537     InsertValues(xRange, aIsDefined, aCharDFNode, false, aHiddenProperties);
538 
539     uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocSh->GetBaseModel(),
540                                                                          uno::UNO_QUERY);
541     uno::Reference<container::XNameAccess> xStyleFamilies
542         = xStyleFamiliesSupplier->getStyleFamilies();
543     OUString sCurrentCharStyle, sCurrentParaStyle, sDisplayName;
544 
545     uno::Reference<container::XNameAccess> xStyleFamily(
546         xStyleFamilies->getByName("CharacterStyles"), uno::UNO_QUERY_THROW);
547     xPropertiesSet->getPropertyValue("CharStyleName") >>= sCurrentCharStyle;
548     xPropertiesSet->getPropertyValue("ParaStyleName") >>= sCurrentParaStyle;
549 
550     if (!sCurrentCharStyle.isEmpty())
551     {
552         xPropertiesSet.set(xStyleFamily->getByName(sCurrentCharStyle), css::uno::UNO_QUERY_THROW);
553         xPropertiesSet->getPropertyValue("DisplayName") >>= sDisplayName;
554         svx::sidebar::TreeNode aCurrentChild;
555         aCurrentChild.sNodeName = sDisplayName;
556         aCurrentChild.NodeType = svx::sidebar::TreeNode::ComplexProperty;
557 
558         InsertValues(xPropertiesSet, aIsDefined, aCurrentChild, false, {});
559 
560         aCharNode.children.push_back(aCurrentChild);
561     }
562 
563     // Collect paragraph direct formatting
564     uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xRange, uno::UNO_QUERY_THROW);
565     uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
566     uno::Reference<text::XTextRange> xThisParagraphRange(xParaEnum->nextElement(), uno::UNO_QUERY);
567     if (xThisParagraphRange.is())
568     {
569         // Collect metadata of the current paragraph
570         MetadataToTreeNode(xThisParagraphRange, aParaDFNode);
571         InsertValues(xThisParagraphRange, aIsDefined, aParaDFNode, false, aHiddenProperties);
572     }
573 
574     xStyleFamily.set(xStyleFamilies->getByName("ParagraphStyles"), uno::UNO_QUERY_THROW);
575 
576     while (!sCurrentParaStyle.isEmpty())
577     {
578         uno::Reference<style::XStyle> xPropertiesStyle(xStyleFamily->getByName(sCurrentParaStyle),
579                                                        uno::UNO_QUERY_THROW);
580         xPropertiesSet.set(xPropertiesStyle, css::uno::UNO_QUERY_THROW);
581         xPropertiesSet->getPropertyValue("DisplayName") >>= sDisplayName;
582         OUString aParentParaStyle = xPropertiesStyle->getParentStyle();
583         svx::sidebar::TreeNode aCurrentChild;
584         aCurrentChild.sNodeName = sDisplayName;
585         aCurrentChild.NodeType = svx::sidebar::TreeNode::ComplexProperty;
586 
587         InsertValues(xPropertiesSet, aIsDefined, aCurrentChild, aParentParaStyle.isEmpty(), {});
588 
589         aParaNode.children.push_back(aCurrentChild);
590         sCurrentParaStyle = aParentParaStyle;
591     }
592 
593     std::reverse(aParaNode.children.begin(),
594                  aParaNode.children.end()); // Parent style should be first then children
595 
596     // Collect bookmarks at character position
597     uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(pDocSh->GetBaseModel(),
598                                                                 uno::UNO_QUERY);
599 
600     uno::Reference<container::XIndexAccess> xBookmarks(xBookmarksSupplier->getBookmarks(),
601                                                        uno::UNO_QUERY);
602     for (sal_Int32 i = 0; i < xBookmarks->getCount(); ++i)
603     {
604         svx::sidebar::TreeNode aCurNode;
605         uno::Reference<text::XTextContent> bookmark;
606         xBookmarks->getByIndex(i) >>= bookmark;
607         uno::Reference<container::XNamed> xBookmark(bookmark, uno::UNO_QUERY);
608 
609         try
610         {
611             uno::Reference<text::XTextRange> bookmarkRange = bookmark->getAnchor();
612             uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xRange->getText(),
613                                                                       uno::UNO_QUERY);
614             if (xTextRangeCompare.is()
615                 && xTextRangeCompare->compareRegionStarts(bookmarkRange, xRange) != -1
616                 && xTextRangeCompare->compareRegionEnds(xRange, bookmarkRange) != -1)
617             {
618                 aCurNode.sNodeName = xBookmark->getName();
619                 aCurNode.NodeType = svx::sidebar::TreeNode::ComplexProperty;
620 
621                 MetadataToTreeNode(xBookmark, aCurNode);
622                 aBookmarksNode.children.push_back(aCurNode);
623             }
624         }
625         catch (const lang::IllegalArgumentException&)
626         {
627         }
628     }
629 
630     /*
631     Display Order :-
632     PARAGRAPH STYLE
633     PARAGRAPH DIRECT FORMATTING
634     CHARACTER STYLE
635     DIRECT FORMATTING
636     BOOKMARKS
637     */
638     aStore.push_back(aParaNode);
639     aStore.push_back(aParaDFNode);
640     aStore.push_back(aCharNode);
641     aStore.push_back(aCharDFNode);
642     if (aBookmarksNode.children.size() > 0)
643         aStore.push_back(aBookmarksNode);
644 }
645 
IMPL_LINK(WriterInspectorTextPanel,AttrChangedNotify,LinkParamNone *,pLink,void)646 IMPL_LINK(WriterInspectorTextPanel, AttrChangedNotify, LinkParamNone*, pLink, void)
647 {
648     if (m_oldLink.IsSet())
649         m_oldLink.Call(pLink);
650 
651     SwDocShell* pDocSh = m_pShell->GetDoc()->GetDocShell();
652     std::vector<svx::sidebar::TreeNode> aStore;
653 
654     if (pDocSh && pDocSh->GetDoc()->GetEditShell()->GetCursor()->GetNode().GetTextNode())
655         UpdateTree(pDocSh, aStore);
656 
657     updateEntries(aStore);
658 }
659 
660 } // end of namespace svx::sidebar
661 
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
663