1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <comphelper/processfactory.hxx>
21 #include <rtl/bootstrap.hxx>
22 #include <config_folders.h>
23 #include <CustomNotebookbarGenerator.hxx>
24 #include <osl/file.hxx>
25 #include <osl/thread.h>
26 #include <vcl/EnumContext.hxx>
27 #include <vcl/settings.hxx>
28 #include <sfx2/viewfrm.hxx>
29 #include <com/sun/star/frame/ModuleManager.hpp>
30 #include <unotools/confignode.hxx>
31 #include <libxml/parser.h>
32 
33 #define aUIPropertiesCount 3
34 
35 using namespace css;
36 
CustomNotebookbarGenerator()37 CustomNotebookbarGenerator::CustomNotebookbarGenerator() {}
38 
lcl_activeAppName(vcl::EnumContext::Application eApp)39 static OUString lcl_activeAppName(vcl::EnumContext::Application eApp)
40 {
41     switch (eApp)
42     {
43         case vcl::EnumContext::Application::Writer:
44             return "ActiveWriter";
45         case vcl::EnumContext::Application::Calc:
46             return "ActiveCalc";
47         case vcl::EnumContext::Application::Impress:
48             return "ActiveImpress";
49         case vcl::EnumContext::Application::Draw:
50             return "ActiveDraw";
51         default:
52             return OUString();
53     }
54 }
55 
lcl_getAppName(vcl::EnumContext::Application eApp)56 static OUString lcl_getAppName(vcl::EnumContext::Application eApp)
57 {
58     switch (eApp)
59     {
60         case vcl::EnumContext::Application::Writer:
61             return "Writer";
62         case vcl::EnumContext::Application::Calc:
63             return "Calc";
64         case vcl::EnumContext::Application::Impress:
65             return "Impress";
66         case vcl::EnumContext::Application::Draw:
67             return "Draw";
68         default:
69             return OUString();
70     }
71 }
72 
getAppNameRegistryPath()73 static OUString getAppNameRegistryPath()
74 {
75     vcl::EnumContext::Application eApp = vcl::EnumContext::Application::Any;
76     const Reference<frame::XFrame>& xFrame
77         = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
78     const Reference<frame::XModuleManager> xModuleManager
79         = frame::ModuleManager::create(::comphelper::getProcessComponentContext());
80     eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
81 
82     OUString sAppName(lcl_getAppName(eApp));
83     return "org.openoffice.Office.UI.ToolbarMode/Applications/" + sAppName;
84 }
85 
customizedUIPathBuffer()86 static OUString customizedUIPathBuffer()
87 {
88     OUString sDirPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE(
89         "bootstrap") ":UserInstallation}/user/config/soffice.cfg/");
90     rtl::Bootstrap::expandMacros(sDirPath);
91     return sDirPath;
92 }
93 
getCustomizedUIPath()94 OUString CustomNotebookbarGenerator::getCustomizedUIPath()
95 {
96     OUString sAppName, sNotebookbarUIFileName;
97     CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
98     return customizedUIPathBuffer() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/"
99            + sNotebookbarUIFileName;
100 }
101 
getOriginalUIPath()102 OUString CustomNotebookbarGenerator::getOriginalUIPath()
103 {
104     OUString sAppName, sNotebookbarUIFileName;
105     CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
106     return AllSettings::GetUIRootDir() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/"
107            + sNotebookbarUIFileName;
108 }
109 
getUIDirPath()110 static OUString getUIDirPath()
111 {
112     OUString sAppName, sNotebookbarUIFileName;
113     CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
114     OUString sUIDirPath
115         = customizedUIPathBuffer() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/";
116     return sUIDirPath;
117 }
118 
getSystemPath(OUString const & sURL)119 OString CustomNotebookbarGenerator::getSystemPath(OUString const& sURL)
120 {
121     if (sURL.isEmpty())
122         return OString();
123     OUString sSystemPathSettings;
124     if (osl_getSystemPathFromFileURL(sURL.pData, &sSystemPathSettings.pData) != osl_File_E_None)
125     {
126         SAL_WARN("cui.customnotebookbar", "Cannot get system path for :" << sURL);
127         return OString();
128     }
129     OString osSystemPathSettings
130         = OUStringToOString(sSystemPathSettings, osl_getThreadTextEncoding());
131     return osSystemPathSettings;
132 }
133 
changeNodeValue(xmlNode * pNodePtr,const char * pProperty,const char * pValue)134 static void changeNodeValue(xmlNode* pNodePtr, const char* pProperty, const char* pValue)
135 {
136     pNodePtr = pNodePtr->xmlChildrenNode;
137     while (pNodePtr)
138     {
139         if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("property"))))
140         {
141             xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("name"));
142             if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>(pProperty))))
143                 xmlNodeSetContent(pNodePtr, reinterpret_cast<const xmlChar*>(pValue));
144             xmlFree(UriValue);
145             break;
146         }
147         pNodePtr = pNodePtr->next;
148     }
149 }
150 
searchNodeAndAttribute(xmlNode * pNodePtr,const char * pUIItemID,const char * pProperty,const char * pValue)151 static void searchNodeAndAttribute(xmlNode* pNodePtr, const char* pUIItemID, const char* pProperty,
152                                    const char* pValue)
153 {
154     pNodePtr = pNodePtr->xmlChildrenNode;
155     while (pNodePtr)
156     {
157         if (pNodePtr->type == XML_ELEMENT_NODE)
158         {
159             if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("object"))))
160             {
161                 xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("id"));
162                 if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>(pUIItemID))))
163                     changeNodeValue(pNodePtr, pProperty, pValue);
164                 xmlFree(UriValue);
165             }
166             searchNodeAndAttribute(pNodePtr, pUIItemID, pProperty, pValue);
167         }
168         pNodePtr = pNodePtr->next;
169     }
170 }
171 
notebookbarXMLParser(const OString & rDocName,const OString & rUIItemID,const OString & rProperty,const OString & rValue)172 static xmlDocPtr notebookbarXMLParser(const OString& rDocName, const OString& rUIItemID,
173                                       const OString& rProperty, const OString& rValue)
174 {
175     xmlDocPtr pDocPtr = xmlParseFile(rDocName.getStr());
176     xmlNodePtr pNodePtr = xmlDocGetRootElement(pDocPtr);
177     searchNodeAndAttribute(pNodePtr, rUIItemID.getStr(), rProperty.getStr(), rValue.getStr());
178     return pDocPtr;
179 }
180 
modifyCustomizedUIFile(const Sequence<OUString> & sUIItemProperties)181 void CustomNotebookbarGenerator::modifyCustomizedUIFile(const Sequence<OUString>& sUIItemProperties)
182 {
183     OString sCustomizedUIPath = getSystemPath(getCustomizedUIPath());
184     for (auto const& aValue : sUIItemProperties)
185     {
186         std::vector<OString> aProperties(aUIPropertiesCount);
187         for (sal_Int32 aIndex = 0; aIndex < aUIPropertiesCount; aIndex++)
188         {
189             sal_Int32 nPos = aIndex;
190             OUString sToken = aValue.getToken(nPos, ',', nPos);
191             aProperties[aIndex] = OUStringToOString(sToken, RTL_TEXTENCODING_UTF8);
192         }
193         xmlDocPtr doc = notebookbarXMLParser(sCustomizedUIPath, aProperties[0], aProperties[1],
194                                              aProperties[2]);
195 
196         if (doc != nullptr)
197         {
198             xmlSaveFormatFile(sCustomizedUIPath.getStr(), doc, 1);
199             xmlFreeDoc(doc);
200         }
201     }
202 }
203 
getFileNameAndAppName(OUString & sAppName,OUString & sNotebookbarUIFileName)204 void CustomNotebookbarGenerator::getFileNameAndAppName(OUString& sAppName,
205                                                        OUString& sNotebookbarUIFileName)
206 {
207     SfxViewFrame* pFrame = SfxViewFrame::Current();
208     if (!pFrame)
209         return;
210 
211     const auto xContext = comphelper::getProcessComponentContext();
212     utl::OConfigurationTreeRoot aRoot(xContext, "org.openoffice.Office.UI.ToolbarMode/", false);
213     const Reference<frame::XFrame>& xFrame = pFrame->GetFrame().GetFrameInterface();
214     const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create(xContext);
215 
216     vcl::EnumContext::Application eApp
217         = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
218     OUString sActiveAppName(lcl_activeAppName(eApp));
219     sAppName = lcl_getAppName(eApp);
220     const Any aValue = aRoot.getNodeValue(sActiveAppName);
221     aValue >>= sNotebookbarUIFileName;
222 }
223 
createCustomizedUIFile()224 void CustomNotebookbarGenerator::createCustomizedUIFile()
225 {
226     OUString sUserUIDir = getUIDirPath();
227     OUString sOriginalUIPath = getOriginalUIPath();
228     OUString sCustomizedUIPath = getCustomizedUIPath();
229 
230     sal_uInt32 nflag = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
231     osl::Directory aDirectory(sUserUIDir);
232     if (aDirectory.open() != osl::FileBase::E_None)
233         osl::Directory::create(sUserUIDir, nflag);
234     else
235         SAL_WARN("cui.customnotebookbar",
236                  "Cannot create the directory or directory was present :" << sUserUIDir);
237 
238     osl::File aFile(sCustomizedUIPath);
239     if (aFile.open(nflag) != osl::FileBase::E_None)
240         osl::File::copy(sOriginalUIPath, sCustomizedUIPath);
241     else
242         SAL_WARN("cui.customnotebookbar",
243                  "Cannot copy the file or file was present :" << sCustomizedUIPath);
244 }
245 
getCustomizedUIItem(OUString sNotebookbarConfigType)246 Sequence<OUString> CustomNotebookbarGenerator::getCustomizedUIItem(OUString sNotebookbarConfigType)
247 {
248     OUString aPath = getAppNameRegistryPath();
249     const utl::OConfigurationTreeRoot aAppNode(::comphelper::getProcessComponentContext(), aPath,
250                                                false);
251 
252     const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes");
253     const utl::OConfigurationNode aModeNode(aModesNode.openNode(sNotebookbarConfigType));
254     const Any aValue = aModeNode.getNodeValue("UIItemProperties");
255     Sequence<OUString> aValues;
256     aValue >>= aValues;
257     return aValues;
258 }
259 
setCustomizedUIItem(Sequence<OUString> sUIItemProperties,OUString sNotebookbarConfigType)260 void CustomNotebookbarGenerator::setCustomizedUIItem(Sequence<OUString> sUIItemProperties,
261                                                      OUString sNotebookbarConfigType)
262 {
263     OUString aPath = getAppNameRegistryPath();
264     const utl::OConfigurationTreeRoot aAppNode(::comphelper::getProcessComponentContext(), aPath,
265                                                true);
266     const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes");
267     const utl::OConfigurationNode aModeNode(aModesNode.openNode(sNotebookbarConfigType));
268 
269     css::uno::Any aUIItemProperties(makeAny(sUIItemProperties));
270     aModeNode.setNodeValue("UIItemProperties", aUIItemProperties);
271     aAppNode.commit();
272 }
273 
274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
275