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 #include <svx/svxids.hrc>
22 #include <doc.hxx>
23 #include <docsh.hxx>
24 #include <wrtsh.hxx>
25 #include <com/sun/star/text/XTextRange.hpp>
26 #include <com/sun/star/text/XTextCursor.hpp>
27 #include <com/sun/star/awt/FontSlant.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
30 #include <com/sun/star/lang/IllegalArgumentException.hpp>
31 
32 #include <unotextrange.hxx>
33 
34 namespace sw::sidebar
35 {
36 VclPtr<vcl::Window> WriterInspectorTextPanel::Create(vcl::Window* pParent,
37                                                      const uno::Reference<frame::XFrame>& rxFrame,
38                                                      SfxBindings* pBindings)
39 {
40     if (pParent == nullptr)
41         throw lang::IllegalArgumentException(
42             "no parent Window given to WriterInspectorTextPanel::Create", nullptr, 0);
43     if (!rxFrame.is())
44         throw lang::IllegalArgumentException("no XFrame given to WriterInspectorTextPanel::Create",
45                                              nullptr, 1);
46     if (pBindings == nullptr)
47         throw lang::IllegalArgumentException(
48             "no SfxBindings given to WriterInspectorTextPanel::Create", nullptr, 2);
49 
50     return VclPtr<WriterInspectorTextPanel>::Create(pParent, rxFrame, pBindings);
51 }
52 
53 WriterInspectorTextPanel::WriterInspectorTextPanel(vcl::Window* pParent,
54                                                    const uno::Reference<frame::XFrame>& rxFrame,
55                                                    SfxBindings* pBindings)
56     : InspectorTextPanel(pParent, rxFrame)
57     , maCharStyle(SID_STYLE_FAMILY1, *pBindings, *this)
58     , maParaStyle(SID_STYLE_FAMILY2, *pBindings, *this)
59 {
60 }
61 
62 static bool GetPropertyValues(const beans::Property rProperty, const uno::Any& rAny,
63                               OUString& rString)
64 {
65     // Hide Asian and Complex properties
66     if (rProperty.Name.indexOf("Asian") != -1 || rProperty.Name.indexOf("Complex") != -1)
67         return false;
68 
69     OUString aValue;
70     double fValue;
71     bool bValue;
72     short sValue;
73     long lValue;
74     awt::FontSlant iValue;
75 
76     rString = rProperty.Name + "     ";
77 
78     if (rAny >>= bValue)
79     {
80         rString += OUString::boolean(bValue);
81     }
82     else if ((rAny >>= aValue) && !(aValue.isEmpty()))
83     {
84         rString += aValue;
85     }
86     else if (rAny >>= iValue)
87     {
88         rString += (iValue == awt::FontSlant_ITALIC) ? OUStringLiteral("italic")
89                                                      : OUStringLiteral("normal");
90     }
91     else if ((rAny >>= lValue) && lValue)
92     {
93         if (rString.indexOf("Color") != -1)
94             rString += "0x" + OUString::number(lValue, 16);
95         else
96             rString += OUString::number(lValue);
97     }
98     else if ((rAny >>= fValue) && fValue)
99     {
100         if (rString.indexOf("Weight") != -1)
101             rString += (fValue > 100) ? OUStringLiteral("bold") : OUStringLiteral("normal");
102         else
103             rString += OUString::number((round(fValue * 100)) / 100.00);
104     }
105     else if ((rAny >>= sValue) && sValue)
106     {
107         rString += OUString::number(sValue);
108     }
109     else
110         return false;
111     return true;
112 }
113 
114 static void UpdateTree(SwDocShell* pDocSh, svx::sidebar::TreeNode& pParentNode,
115                        std::unordered_map<OUString, bool>& maIsDefined, StyleType sType)
116 {
117     SwDoc* pDoc = pDocSh->GetDoc();
118     SwPaM* pCursor = pDoc->GetEditShell()->GetCursor();
119 
120     uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocSh->GetBaseModel(),
121                                                                          uno::UNO_QUERY);
122     uno::Reference<container::XNameAccess> xStyleFamilies
123         = xStyleFamiliesSupplier->getStyleFamilies();
124     uno::Reference<container::XNameAccess> xStyleFamily;
125 
126     if (sType == CHARACTERSTYLES)
127         xStyleFamilies->getByName("CharacterStyles") >>= xStyleFamily;
128     else
129         xStyleFamilies->getByName("ParagraphStyles") >>= xStyleFamily;
130 
131     uno::Reference<text::XTextCursor> xCursor = dynamic_cast<text::XTextCursor*>(pCursor);
132     uno::Reference<text::XTextRange> xRange(
133         SwXTextRange::CreateXTextRange(*pDoc, *pCursor->GetPoint(), nullptr));
134     uno::Reference<beans::XPropertySet> properties(xRange, uno::UNO_QUERY_THROW);
135 
136     OUString sCurrentStyleName, sDisplayName;
137     if (sType == CHARACTERSTYLES)
138         properties->getPropertyValue("CharStyleName") >>= sCurrentStyleName;
139     else
140         properties->getPropertyValue("ParaStyleName") >>= sCurrentStyleName;
141 
142     if (sCurrentStyleName.isEmpty())
143         sCurrentStyleName = "Standard";
144 
145     while (true)
146     {
147         uno::Reference<style::XStyle> xProp1;
148         uno::Reference<beans::XPropertySet> xProp1Set;
149         uno::Reference<beans::XPropertyState> xProp1State;
150         xStyleFamily->getByName(sCurrentStyleName) >>= xProp1;
151         xStyleFamily->getByName(sCurrentStyleName) >>= xProp1Set;
152         xStyleFamily->getByName(sCurrentStyleName) >>= xProp1State;
153         OUString aParentCharStyle = xProp1->getParentStyle();
154         xProp1Set->getPropertyValue("DisplayName") >>= sDisplayName;
155         svx::sidebar::TreeNode pCurrentChild;
156         pCurrentChild.sNodeName = sDisplayName;
157 
158         if (aParentCharStyle.isEmpty())
159         {
160             break; // when current style is "Standard" there is no parent
161         }
162 
163         const uno::Sequence<beans::Property> aProperties
164             = xProp1Set->getPropertySetInfo()->getProperties();
165         const uno::Reference<beans::XPropertySet> xProp2Set(
166             xStyleFamily->getByName(aParentCharStyle), uno::UNO_QUERY);
167 
168         try
169         {
170             for (const beans::Property& rProperty : aProperties)
171             {
172                 OUString sPropName = rProperty.Name;
173                 // If property's current value equals default value
174                 if (xProp1Set->getPropertyValue(sPropName)
175                     == xProp1State->getPropertyDefault(sPropName))
176                     continue;
177 
178                 if (maIsDefined[sPropName])
179                     continue;
180 
181                 if (xProp1Set->getPropertyValue(sPropName)
182                     != xProp2Set->getPropertyValue(sPropName))
183                 {
184                     maIsDefined[sPropName] = true;
185                     OUString aPropertyValuePair;
186                     const uno::Any aAny = xProp1Set->getPropertyValue(sPropName);
187                     GetPropertyValues(rProperty, aAny, aPropertyValuePair);
188                     if (!aPropertyValuePair.isEmpty())
189                     {
190                         svx::sidebar::TreeNode pTemp;
191                         pTemp.sNodeName = aPropertyValuePair;
192                         pCurrentChild.children.push_back(pTemp);
193                     }
194                 }
195             }
196         }
197         catch (const uno::Exception&)
198         {
199             //do nothing
200         }
201 
202         pParentNode.children.emplace_back(pCurrentChild);
203         sCurrentStyleName = aParentCharStyle;
204     }
205 
206     uno::Reference<beans::XPropertySet> aProp1Set;
207     uno::Reference<beans::XPropertyState> aProp1State;
208     xStyleFamily->getByName(sCurrentStyleName) >>= aProp1Set;
209     xStyleFamily->getByName(sCurrentStyleName) >>= aProp1State;
210 
211     const uno::Sequence<beans::Property> aProperties
212         = aProp1Set->getPropertySetInfo()->getProperties();
213     svx::sidebar::TreeNode pCurrentChild;
214     pCurrentChild.sNodeName = sDisplayName;
215 
216     for (const beans::Property& rProperty : aProperties)
217     {
218         OUString aPropertyValuePair, sPropName = rProperty.Name;
219         if (aProp1Set->getPropertyValue(sPropName) == aProp1State->getPropertyDefault(sPropName))
220             continue;
221         if (maIsDefined[sPropName])
222             continue;
223         maIsDefined[sPropName] = true;
224 
225         const uno::Any aAny = aProp1Set->getPropertyValue(sPropName);
226         if (GetPropertyValues(rProperty, aAny, aPropertyValuePair))
227         {
228             if (!aPropertyValuePair.isEmpty())
229             {
230                 svx::sidebar::TreeNode pTemp;
231                 pTemp.sNodeName = aPropertyValuePair;
232                 pCurrentChild.children.push_back(pTemp);
233             }
234         }
235     }
236 
237     pParentNode.children.emplace_back(pCurrentChild);
238     std::reverse(pParentNode.children.begin(), pParentNode.children.end());
239 }
240 
241 void WriterInspectorTextPanel::NotifyItemUpdate(const sal_uInt16 nSId,
242                                                 const SfxItemState /*eState*/,
243                                                 const SfxPoolItem* /*pState*/)
244 {
245     SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current());
246     std::vector<svx::sidebar::TreeNode> aStore;
247     std::unordered_map<OUString, bool> maIsDefined;
248 
249     switch (nSId)
250     {
251         case SID_STYLE_FAMILY1:
252         case SID_STYLE_FAMILY2:
253         {
254             if (pDocSh)
255             {
256                 /*
257                 First check in the property set of Character Styles
258                 (as CS has higher priority over PS), then look into
259                 property set of Paragraph Styles;
260                 */
261                 svx::sidebar::TreeNode pTempChar;
262                 pTempChar.sNodeName = "CHARACTER STYLES";
263                 UpdateTree(pDocSh, pTempChar, maIsDefined, CHARACTERSTYLES);
264                 svx::sidebar::TreeNode pTempPara;
265                 pTempPara.sNodeName = "PARAGRAPH STYLES";
266                 UpdateTree(pDocSh, pTempPara, maIsDefined, PARAGRAPHSTYLES);
267 
268                 /*
269                 Order:-
270                 PARAGRAPH STYLES
271                 CHARACTER STYLES
272                 DEFAULT FORMATTING
273                 */
274                 aStore.push_back(pTempPara);
275                 aStore.push_back(pTempChar);
276             }
277         }
278         break;
279     }
280 
281     updateEntries(aStore);
282 }
283 
284 } // end of namespace svx::sidebar
285 
286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
287