/src/libreoffice/sfx2/source/sidebar/ResourceManager.cxx
Line | Count | Source (jump to first uncovered line) |
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 <sidebar/DeckDescriptor.hxx> |
21 | | #include <sidebar/PanelDescriptor.hxx> |
22 | | #include <sfx2/sidebar/ResourceManager.hxx> |
23 | | #include <sidebar/Tools.hxx> |
24 | | |
25 | | #include <officecfg/Office/Common.hxx> |
26 | | #include <officecfg/Office/UI/Sidebar.hxx> |
27 | | #include <unotools/confignode.hxx> |
28 | | #include <comphelper/lok.hxx> |
29 | | #include <comphelper/processfactory.hxx> |
30 | | #include <comphelper/namedvaluecollection.hxx> |
31 | | #include <comphelper/sequence.hxx> |
32 | | #include <comphelper/types.hxx> |
33 | | |
34 | | #include <comphelper/diagnose_ex.hxx> |
35 | | #include <sal/log.hxx> |
36 | | #include <o3tl/string_view.hxx> |
37 | | |
38 | | #include <com/sun/star/frame/ModuleManager.hpp> |
39 | | #include <com/sun/star/ui/XSidebarPanel.hpp> |
40 | | #include <com/sun/star/ui/XUpdateModel.hpp> |
41 | | |
42 | | #include <map> |
43 | | |
44 | | using namespace css; |
45 | | using namespace css::uno; |
46 | | |
47 | | namespace sfx2::sidebar { |
48 | | |
49 | | namespace |
50 | | { |
51 | | |
52 | | OUString getString(utl::OConfigurationNode const & aNode, const OUString& rNodeName) |
53 | 0 | { |
54 | 0 | return comphelper::getString(aNode.getNodeValue(rNodeName)); |
55 | 0 | } |
56 | | sal_Int32 getInt32(utl::OConfigurationNode const & aNode, const OUString& rNodeName) |
57 | 0 | { |
58 | 0 | return comphelper::getINT32(aNode.getNodeValue(rNodeName)); |
59 | 0 | } |
60 | | bool getBool(utl::OConfigurationNode const & aNode, const OUString& rNodeName) |
61 | 0 | { |
62 | 0 | return comphelper::getBOOL(aNode.getNodeValue(rNodeName)); |
63 | 0 | } |
64 | | |
65 | | css::uno::Sequence<OUString> BuildContextList (const ContextList& rContextList) |
66 | 0 | { |
67 | 0 | const ::std::vector<ContextList::Entry>& entries = rContextList.GetEntries(); |
68 | |
|
69 | 0 | css::uno::Sequence<OUString> result(entries.size()); |
70 | 0 | auto resultRange = asNonConstRange(result); |
71 | 0 | tools::Long i = 0; |
72 | |
|
73 | 0 | for (auto const& entry : entries) |
74 | 0 | { |
75 | 0 | OUString appName = entry.maContext.msApplication; |
76 | 0 | OUString contextName = entry.maContext.msContext; |
77 | 0 | OUString menuCommand = entry.msMenuCommand; |
78 | |
|
79 | 0 | OUString visibility; |
80 | 0 | if (entry.mbIsInitiallyVisible) |
81 | 0 | visibility = "visible"; |
82 | 0 | else |
83 | 0 | visibility = "hidden"; |
84 | |
|
85 | 0 | OUString element = appName + ", " + contextName +", " + visibility; |
86 | |
|
87 | 0 | if (!menuCommand.isEmpty()) |
88 | 0 | element += ", "+menuCommand; |
89 | |
|
90 | 0 | resultRange[i] = element; |
91 | |
|
92 | 0 | ++i; |
93 | 0 | } |
94 | |
|
95 | 0 | return result; |
96 | |
|
97 | 0 | } |
98 | | |
99 | | } //end anonymous namespace |
100 | | |
101 | | ResourceManager::ResourceManager() |
102 | 0 | { |
103 | 0 | ReadDeckList(); |
104 | 0 | ReadPanelList(); |
105 | 0 | ReadLastActive(); |
106 | 0 | } |
107 | | |
108 | | ResourceManager::~ResourceManager() |
109 | 0 | { |
110 | 0 | } |
111 | | |
112 | | void ResourceManager::InitDeckContext(const Context& rContext) |
113 | 0 | { |
114 | 0 | for (auto const& deck : maDecks) |
115 | 0 | { |
116 | 0 | const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext); |
117 | |
|
118 | 0 | bool bIsEnabled; |
119 | 0 | if (pMatchingEntry) |
120 | 0 | bIsEnabled = pMatchingEntry->mbIsInitiallyVisible; |
121 | 0 | else |
122 | 0 | bIsEnabled = false; |
123 | |
|
124 | 0 | deck->mbIsEnabled = bIsEnabled; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | std::shared_ptr<DeckDescriptor> ResourceManager::ImplGetDeckDescriptor(std::u16string_view rsDeckId) const |
129 | 0 | { |
130 | 0 | for (auto const& deck : maDecks) |
131 | 0 | { |
132 | 0 | if (deck->mbHiddenInViewerMode && officecfg::Office::Common::Misc::ViewerAppMode::get()) |
133 | 0 | continue; |
134 | 0 | if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get()) |
135 | 0 | continue; |
136 | 0 | if (deck->msId == rsDeckId) |
137 | 0 | return deck; |
138 | 0 | } |
139 | 0 | return nullptr; |
140 | 0 | } |
141 | | |
142 | | std::shared_ptr<DeckDescriptor> ResourceManager::GetDeckDescriptor(std::u16string_view rsDeckId) const |
143 | 0 | { |
144 | 0 | return ImplGetDeckDescriptor( rsDeckId ); |
145 | 0 | } |
146 | | |
147 | | std::shared_ptr<PanelDescriptor> ResourceManager::ImplGetPanelDescriptor(std::u16string_view rsPanelId) const |
148 | 0 | { |
149 | 0 | for (auto const& panel : maPanels) |
150 | 0 | { |
151 | 0 | if (panel->msId == rsPanelId) |
152 | 0 | return panel; |
153 | 0 | } |
154 | 0 | return nullptr; |
155 | 0 | } |
156 | | |
157 | | std::shared_ptr<PanelDescriptor> ResourceManager::GetPanelDescriptor(std::u16string_view rsPanelId) const |
158 | 0 | { |
159 | 0 | return ImplGetPanelDescriptor( rsPanelId ); |
160 | 0 | } |
161 | | |
162 | | const ResourceManager::DeckContextDescriptorContainer& ResourceManager::GetMatchingDecks ( |
163 | | DeckContextDescriptorContainer& rDecks, |
164 | | const Context& rContext, |
165 | | const bool bIsDocumentReadOnly, |
166 | | const Reference<frame::XController>& rxController) |
167 | 0 | { |
168 | 0 | ReadLegacyAddons(rxController); |
169 | |
|
170 | 0 | std::multimap<sal_Int32,DeckContextDescriptor> aOrderedIds; |
171 | 0 | for (auto const& deck : maDecks) |
172 | 0 | { |
173 | 0 | if (deck->mbHiddenInViewerMode && officecfg::Office::Common::Misc::ViewerAppMode::get()) |
174 | 0 | continue; |
175 | 0 | if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get()) |
176 | 0 | continue; |
177 | | |
178 | 0 | const DeckDescriptor& rDeckDescriptor (*deck); |
179 | 0 | if (rDeckDescriptor.maContextList.GetMatch(rContext) == nullptr) |
180 | 0 | continue; |
181 | | |
182 | 0 | DeckContextDescriptor aDeckContextDescriptor; |
183 | 0 | aDeckContextDescriptor.msId = rDeckDescriptor.msId; |
184 | |
|
185 | 0 | aDeckContextDescriptor.mbIsEnabled = (! bIsDocumentReadOnly || IsDeckEnabled(rDeckDescriptor.msId, rContext, rxController) ) |
186 | 0 | && rDeckDescriptor.mbIsEnabled; |
187 | | |
188 | |
|
189 | 0 | aOrderedIds.emplace(rDeckDescriptor.mnOrderIndex, aDeckContextDescriptor); |
190 | 0 | } |
191 | |
|
192 | 0 | for (auto const& orderId : aOrderedIds) |
193 | 0 | { |
194 | 0 | rDecks.push_back(orderId.second); |
195 | 0 | } |
196 | |
|
197 | 0 | return rDecks; |
198 | 0 | } |
199 | | |
200 | | const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels ( |
201 | | PanelContextDescriptorContainer& rPanelIds, |
202 | | const Context& rContext, |
203 | | std::u16string_view sDeckId, |
204 | | const Reference<frame::XController>& rxController) |
205 | 0 | { |
206 | 0 | ReadLegacyAddons(rxController); |
207 | |
|
208 | 0 | std::multimap<sal_Int32, PanelContextDescriptor> aOrderedIds; |
209 | 0 | for (auto const& panel : maPanels) |
210 | 0 | { |
211 | 0 | const PanelDescriptor& rPanelDescriptor (*panel); |
212 | 0 | if (rPanelDescriptor.mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get()) |
213 | 0 | continue; |
214 | 0 | if ( rPanelDescriptor.msDeckId != sDeckId ) |
215 | 0 | continue; |
216 | | |
217 | 0 | const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext); |
218 | 0 | if (pEntry == nullptr) |
219 | 0 | continue; |
220 | | |
221 | 0 | PanelContextDescriptor aPanelContextDescriptor; |
222 | 0 | aPanelContextDescriptor.msId = rPanelDescriptor.msId; |
223 | 0 | aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand; |
224 | 0 | aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible; |
225 | 0 | aPanelContextDescriptor.mbShowForReadOnlyDocuments = rPanelDescriptor.mbShowForReadOnlyDocuments; |
226 | 0 | aOrderedIds.emplace(rPanelDescriptor.mnOrderIndex, aPanelContextDescriptor); |
227 | 0 | } |
228 | |
|
229 | 0 | for (auto const& orderId : aOrderedIds) |
230 | 0 | { |
231 | 0 | rPanelIds.push_back(orderId.second); |
232 | 0 | } |
233 | |
|
234 | 0 | return rPanelIds; |
235 | 0 | } |
236 | | |
237 | | const OUString& ResourceManager::GetLastActiveDeck( const Context& rContext ) |
238 | 0 | { |
239 | 0 | assert(!comphelper::LibreOfficeKit::isActive()); |
240 | 0 | if( maLastActiveDecks.find( rContext.msApplication ) == maLastActiveDecks.end()) |
241 | 0 | return maLastActiveDecks[u"any"_ustr]; |
242 | 0 | else |
243 | 0 | return maLastActiveDecks[rContext.msApplication]; |
244 | 0 | } |
245 | | |
246 | | void ResourceManager::SetLastActiveDeck( const Context& rContext, const OUString &rsDeckId ) |
247 | 0 | { |
248 | 0 | assert(!comphelper::LibreOfficeKit::isActive()); |
249 | 0 | maLastActiveDecks[rContext.msApplication] = rsDeckId; |
250 | 0 | } |
251 | | |
252 | | void ResourceManager::ReadDeckList() |
253 | 0 | { |
254 | 0 | const utl::OConfigurationTreeRoot aDeckRootNode( |
255 | 0 | comphelper::getProcessComponentContext(), |
256 | 0 | u"org.openoffice.Office.UI.Sidebar/Content/DeckList"_ustr, |
257 | 0 | false); |
258 | 0 | if (!aDeckRootNode.isValid()) |
259 | 0 | return; |
260 | | |
261 | 0 | const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames()); |
262 | 0 | maDecks.clear(); |
263 | 0 | for (const OUString& aDeckName : aDeckNodeNames) |
264 | 0 | { |
265 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
266 | 0 | { |
267 | | // Hide these decks in LOK as they aren't fully functional. |
268 | 0 | if (aDeckName == "GalleryDeck") |
269 | 0 | continue; |
270 | 0 | } |
271 | | |
272 | 0 | const utl::OConfigurationNode aDeckNode(aDeckRootNode.openNode(aDeckName)); |
273 | 0 | if (!aDeckNode.isValid()) |
274 | 0 | continue; |
275 | | |
276 | 0 | maDecks.push_back(std::make_shared<DeckDescriptor>()); |
277 | 0 | DeckDescriptor& rDeckDescriptor (*maDecks.back()); |
278 | |
|
279 | 0 | rDeckDescriptor.msTitle = getString(aDeckNode, u"Title"_ustr); |
280 | 0 | rDeckDescriptor.msId = getString(aDeckNode, u"Id"_ustr); |
281 | 0 | rDeckDescriptor.msIconURL = getString(aDeckNode, u"IconURL"_ustr); |
282 | 0 | rDeckDescriptor.msHighContrastIconURL = getString(aDeckNode, u"HighContrastIconURL"_ustr); |
283 | 0 | rDeckDescriptor.msTitleBarIconURL = getString(aDeckNode, u"TitleBarIconURL"_ustr); |
284 | 0 | rDeckDescriptor.msHighContrastTitleBarIconURL = getString(aDeckNode, u"HighContrastTitleBarIconURL"_ustr); |
285 | 0 | rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; |
286 | 0 | rDeckDescriptor.msHelpId = "SIDEBAR_" + rDeckDescriptor.msId.toAsciiUpperCase(); |
287 | 0 | rDeckDescriptor.mnOrderIndex = getInt32(aDeckNode, u"OrderIndex"_ustr); |
288 | 0 | rDeckDescriptor.mbExperimental = getBool(aDeckNode, u"IsExperimental"_ustr); |
289 | 0 | rDeckDescriptor.mbHiddenInViewerMode = getBool(aDeckNode, u"HiddenInViewer"_ustr); |
290 | |
|
291 | 0 | rDeckDescriptor.msNodeName = aDeckName; |
292 | |
|
293 | 0 | ReadContextList( |
294 | 0 | aDeckNode, |
295 | 0 | rDeckDescriptor.maContextList, |
296 | 0 | OUString()); |
297 | |
|
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | void ResourceManager::SaveDecksSettings(const Context& rContext) |
302 | 0 | { |
303 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
304 | 0 | return; |
305 | | |
306 | 0 | for (auto const& deck : maDecks) |
307 | 0 | { |
308 | 0 | const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext); |
309 | 0 | if (pMatchingEntry) |
310 | 0 | { |
311 | 0 | std::shared_ptr<DeckDescriptor> xDeckDesc = GetDeckDescriptor(deck->msId); |
312 | 0 | if (xDeckDesc) |
313 | 0 | SaveDeckSettings(xDeckDesc.get()); |
314 | 0 | } |
315 | |
|
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | void ResourceManager::SaveDeckSettings(const DeckDescriptor* pDeckDesc) |
320 | 0 | { |
321 | 0 | const utl::OConfigurationTreeRoot aDeckRootNode( |
322 | 0 | comphelper::getProcessComponentContext(), |
323 | 0 | u"org.openoffice.Office.UI.Sidebar/Content/DeckList"_ustr, |
324 | 0 | true); |
325 | 0 | if (!aDeckRootNode.isValid()) |
326 | 0 | return; |
327 | | |
328 | | // save deck settings |
329 | | |
330 | 0 | ::uno::Sequence< OUString > sContextList = BuildContextList(pDeckDesc->maContextList); |
331 | |
|
332 | 0 | utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(pDeckDesc->msNodeName)); |
333 | |
|
334 | 0 | css::uno::Any aTitle(Any(pDeckDesc->msTitle)); |
335 | 0 | css::uno::Any aOrder(Any(pDeckDesc->mnOrderIndex)); |
336 | 0 | css::uno::Any aContextList(sContextList); |
337 | |
|
338 | 0 | bool bChanged = false; |
339 | 0 | if (aTitle != aDeckNode.getNodeValue(u"Title"_ustr)) |
340 | 0 | { |
341 | 0 | aDeckNode.setNodeValue(u"Title"_ustr, aTitle); |
342 | 0 | bChanged = true; |
343 | 0 | } |
344 | 0 | if (aOrder != aDeckNode.getNodeValue(u"OrderIndex"_ustr)) |
345 | 0 | { |
346 | 0 | aDeckNode.setNodeValue(u"OrderIndex"_ustr, aOrder); |
347 | 0 | bChanged = true; |
348 | 0 | } |
349 | 0 | if (aContextList != aDeckNode.getNodeValue(u"ContextList"_ustr)) |
350 | 0 | { |
351 | 0 | aDeckNode.setNodeValue(u"ContextList"_ustr, aContextList); |
352 | 0 | bChanged = true; |
353 | 0 | } |
354 | |
|
355 | 0 | if (bChanged) |
356 | 0 | aDeckRootNode.commit(); |
357 | | |
358 | | // save panel settings |
359 | |
|
360 | 0 | const utl::OConfigurationTreeRoot aPanelRootNode( |
361 | 0 | comphelper::getProcessComponentContext(), |
362 | 0 | u"org.openoffice.Office.UI.Sidebar/Content/PanelList"_ustr, |
363 | 0 | true); |
364 | |
|
365 | 0 | if (!aPanelRootNode.isValid()) |
366 | 0 | return; |
367 | | |
368 | 0 | if (!pDeckDesc->mpDeck) // the deck has not been edited |
369 | 0 | return; |
370 | | |
371 | 0 | SharedPanelContainer rPanels = pDeckDesc->mpDeck->GetPanels(); |
372 | |
|
373 | 0 | bChanged = false; |
374 | 0 | for (auto const& panel : rPanels) |
375 | 0 | { |
376 | 0 | OUString panelId = panel->GetId(); |
377 | 0 | std::shared_ptr<PanelDescriptor> xPanelDesc = GetPanelDescriptor(panelId); |
378 | |
|
379 | 0 | ::uno::Sequence< OUString > sPanelContextList = BuildContextList(xPanelDesc->maContextList); |
380 | |
|
381 | 0 | utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(xPanelDesc->msNodeName)); |
382 | |
|
383 | 0 | aTitle <<= xPanelDesc->msTitle; |
384 | 0 | aOrder <<= xPanelDesc->mnOrderIndex; |
385 | 0 | aContextList <<= sPanelContextList; |
386 | |
|
387 | 0 | if (aTitle != aPanelNode.getNodeValue(u"Title"_ustr)) |
388 | 0 | { |
389 | 0 | aPanelNode.setNodeValue(u"Title"_ustr, aTitle); |
390 | 0 | bChanged = true; |
391 | 0 | } |
392 | 0 | if (aOrder != aPanelNode.getNodeValue(u"OrderIndex"_ustr)) |
393 | 0 | { |
394 | 0 | aPanelNode.setNodeValue(u"OrderIndex"_ustr, aOrder); |
395 | 0 | bChanged = true; |
396 | 0 | } |
397 | 0 | if (aContextList != aPanelNode.getNodeValue(u"ContextList"_ustr)) |
398 | 0 | { |
399 | 0 | aPanelNode.setNodeValue(u"ContextList"_ustr, aContextList); |
400 | 0 | bChanged = true; |
401 | 0 | } |
402 | 0 | } |
403 | |
|
404 | 0 | if (bChanged) |
405 | 0 | aPanelRootNode.commit(); |
406 | 0 | } |
407 | | |
408 | | void ResourceManager::SaveLastActiveDeck(const Context& rContext, const OUString& rActiveDeck) |
409 | 0 | { |
410 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
411 | 0 | return; |
412 | | |
413 | 0 | maLastActiveDecks[rContext.msApplication] = rActiveDeck; |
414 | |
|
415 | 0 | std::set<OUString> aLastActiveDecks; |
416 | 0 | for ( auto const & rEntry : maLastActiveDecks ) |
417 | 0 | aLastActiveDecks.insert( rEntry.first + "," + rEntry.second); |
418 | |
|
419 | 0 | std::shared_ptr<comphelper::ConfigurationChanges> cfgWriter( comphelper::ConfigurationChanges::create() ); |
420 | |
|
421 | 0 | officecfg::Office::UI::Sidebar::Content::LastActiveDeck::set(comphelper::containerToSequence(aLastActiveDecks), cfgWriter); |
422 | 0 | cfgWriter->commit(); |
423 | |
|
424 | 0 | } |
425 | | |
426 | | void ResourceManager::ReadPanelList() |
427 | 0 | { |
428 | 0 | const utl::OConfigurationTreeRoot aPanelRootNode( |
429 | 0 | comphelper::getProcessComponentContext(), |
430 | 0 | u"org.openoffice.Office.UI.Sidebar/Content/PanelList"_ustr, |
431 | 0 | false); |
432 | 0 | if (!aPanelRootNode.isValid()) |
433 | 0 | return; |
434 | | |
435 | 0 | const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames()); |
436 | 0 | maPanels.clear(); |
437 | 0 | for (const auto& rPanelNodeName : aPanelNodeNames) |
438 | 0 | { |
439 | 0 | const utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(rPanelNodeName)); |
440 | 0 | if (!aPanelNode.isValid()) |
441 | 0 | continue; |
442 | | |
443 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
444 | 0 | { |
445 | | // Hide these panels in LOK as they aren't fully functional. |
446 | 0 | OUString aPanelId = getString(aPanelNode, u"Id"_ustr); |
447 | 0 | if (aPanelId == "PageStylesPanel" || aPanelId == "PageHeaderPanel" |
448 | 0 | || aPanelId == "PageFooterPanel") |
449 | 0 | continue; |
450 | 0 | } |
451 | | |
452 | 0 | maPanels.push_back(std::make_shared<PanelDescriptor>()); |
453 | 0 | PanelDescriptor& rPanelDescriptor(*maPanels.back()); |
454 | |
|
455 | 0 | rPanelDescriptor.msTitle = getString(aPanelNode, u"Title"_ustr); |
456 | 0 | rPanelDescriptor.mbIsTitleBarOptional = getBool(aPanelNode, u"TitleBarIsOptional"_ustr); |
457 | 0 | rPanelDescriptor.msId = getString(aPanelNode, u"Id"_ustr); |
458 | 0 | rPanelDescriptor.msDeckId = getString(aPanelNode, u"DeckId"_ustr); |
459 | 0 | rPanelDescriptor.msTitleBarIconURL = getString(aPanelNode, u"TitleBarIconURL"_ustr); |
460 | 0 | rPanelDescriptor.msHighContrastTitleBarIconURL = getString(aPanelNode, u"HighContrastTitleBarIconURL"_ustr); |
461 | 0 | rPanelDescriptor.msImplementationURL = getString(aPanelNode, u"ImplementationURL"_ustr); |
462 | 0 | rPanelDescriptor.mnOrderIndex = getInt32(aPanelNode, u"OrderIndex"_ustr); |
463 | 0 | rPanelDescriptor.mbShowForReadOnlyDocuments = getBool(aPanelNode, u"ShowForReadOnlyDocument"_ustr); |
464 | 0 | rPanelDescriptor.mbWantsCanvas = getBool(aPanelNode, u"WantsCanvas"_ustr); |
465 | 0 | rPanelDescriptor.mbWantsAWT = getBool(aPanelNode, u"WantsAWT"_ustr); |
466 | 0 | rPanelDescriptor.mbExperimental = getBool(aPanelNode, u"IsExperimental"_ustr); |
467 | 0 | const OUString sDefaultMenuCommand(getString(aPanelNode, u"DefaultMenuCommand"_ustr)); |
468 | |
|
469 | 0 | rPanelDescriptor.msNodeName = rPanelNodeName; |
470 | |
|
471 | 0 | ReadContextList(aPanelNode, rPanelDescriptor.maContextList, sDefaultMenuCommand); |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | void ResourceManager::ReadLastActive() |
476 | 0 | { |
477 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
478 | 0 | return; |
479 | | |
480 | 0 | const Sequence <OUString> aLastActive (officecfg::Office::UI::Sidebar::Content::LastActiveDeck::get()); |
481 | |
|
482 | 0 | for (const auto& rDeckInfo : aLastActive) |
483 | 0 | { |
484 | 0 | sal_Int32 nCharIdx = rDeckInfo.lastIndexOf(','); |
485 | 0 | if ( nCharIdx <= 0 || (nCharIdx == rDeckInfo.getLength() - 1) ) |
486 | 0 | { |
487 | 0 | SAL_WARN("sfx.sidebar", "Expecting 2 values separated by comma"); |
488 | 0 | continue; |
489 | 0 | } |
490 | | |
491 | 0 | const OUString sApplicationName = rDeckInfo.copy( 0, nCharIdx ); |
492 | 0 | vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName)); |
493 | 0 | const OUString sLastUsed = rDeckInfo.copy( nCharIdx + 1 ); |
494 | | |
495 | | // guard against garbage in place of application |
496 | 0 | if (eApplication != vcl::EnumContext::Application::NONE) |
497 | 0 | maLastActiveDecks.insert( std::make_pair(sApplicationName, sLastUsed ) ); |
498 | 0 | } |
499 | | |
500 | | // Set up a default for Math - will do nothing if already set |
501 | 0 | for (const auto& aOverrideDeck : GetDeckOverrides()) |
502 | 0 | maLastActiveDecks.emplace(aOverrideDeck.first, aOverrideDeck.second); |
503 | 0 | } |
504 | | |
505 | | void ResourceManager::SetupOverrides() |
506 | 0 | { |
507 | 0 | maApplicationDeckOverrides = { |
508 | 0 | { vcl::EnumContext::GetApplicationName(vcl::EnumContext::Application::Formula), "ElementsDeck" } |
509 | 0 | }; |
510 | 0 | } |
511 | | |
512 | | void ResourceManager::ReadContextList ( |
513 | | const utl::OConfigurationNode& rParentNode, |
514 | | ContextList& rContextList, |
515 | | const OUString& rsDefaultMenuCommand) |
516 | 0 | { |
517 | 0 | const Any aValue = rParentNode.getNodeValue(u"ContextList"_ustr); |
518 | 0 | Sequence<OUString> aValues; |
519 | 0 | if (!(aValue >>= aValues)) |
520 | 0 | return; |
521 | | |
522 | 0 | for (const OUString& sValue : aValues) |
523 | 0 | { |
524 | 0 | sal_Int32 nCharacterIndex (0); |
525 | 0 | const OUString sApplicationName (o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex))); |
526 | 0 | if (nCharacterIndex < 0) |
527 | 0 | { |
528 | 0 | if (sApplicationName.getLength() == 0) |
529 | 0 | { |
530 | | // This is a valid case: in the XML file the separator |
531 | | // was used as terminator. Using it in the last line |
532 | | // creates an additional but empty entry. |
533 | 0 | break; |
534 | 0 | } |
535 | 0 | else |
536 | 0 | { |
537 | 0 | SAL_WARN("sfx.sidebar", "expecting three or four values per ContextList entry, separated by comma, entries: " << aValues); |
538 | 0 | continue; |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | 0 | const OUString sContextName(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex))); |
543 | 0 | if (nCharacterIndex < 0) |
544 | 0 | { |
545 | 0 | SAL_WARN("sfx.sidebar", "expecting three or four values per ContextList entry, separated by comma"); |
546 | 0 | continue; |
547 | 0 | } |
548 | | |
549 | 0 | const std::u16string_view sInitialState(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex))); |
550 | | |
551 | | // The fourth argument is optional. |
552 | 0 | const OUString sMenuCommandOverride( |
553 | 0 | nCharacterIndex < 0 |
554 | 0 | ? OUString() |
555 | 0 | : OUString(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)))); |
556 | |
|
557 | 0 | const OUString sMenuCommand( |
558 | 0 | sMenuCommandOverride.getLength() > 0 |
559 | 0 | ? (sMenuCommandOverride == "none" |
560 | 0 | ? OUString() |
561 | 0 | : sMenuCommandOverride) |
562 | 0 | : rsDefaultMenuCommand); |
563 | | |
564 | | // Setup a list of application enums. Note that the |
565 | | // application name may result in more than one value (eg |
566 | | // DrawImpress will result in two enums, one for Draw and one |
567 | | // for Impress). |
568 | 0 | std::vector<vcl::EnumContext::Application> aApplications; |
569 | 0 | vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName)); |
570 | |
|
571 | 0 | if (eApplication == vcl::EnumContext::Application::NONE |
572 | 0 | && sApplicationName != vcl::EnumContext::GetApplicationName(vcl::EnumContext::Application::NONE)) |
573 | 0 | { |
574 | | // Handle some special names: abbreviations that make |
575 | | // context descriptions more readable. |
576 | 0 | if (sApplicationName == "Writer") |
577 | 0 | aApplications.push_back(vcl::EnumContext::Application::Writer); |
578 | 0 | else if (sApplicationName == "Calc") |
579 | 0 | aApplications.push_back(vcl::EnumContext::Application::Calc); |
580 | 0 | else if (sApplicationName == "Draw") |
581 | 0 | aApplications.push_back(vcl::EnumContext::Application::Draw); |
582 | 0 | else if (sApplicationName == "Impress") |
583 | 0 | aApplications.push_back(vcl::EnumContext::Application::Impress); |
584 | 0 | else if (sApplicationName == "Chart") |
585 | 0 | aApplications.push_back(vcl::EnumContext::Application::Chart); |
586 | 0 | else if (sApplicationName == "Math") |
587 | 0 | aApplications.push_back(vcl::EnumContext::Application::Formula); |
588 | 0 | else if (sApplicationName == "DrawImpress") |
589 | 0 | { |
590 | | // A special case among the special names: it is |
591 | | // common to use the same context descriptions for |
592 | | // both Draw and Impress. This special case helps to |
593 | | // avoid duplication in the .xcu file. |
594 | 0 | aApplications.push_back(vcl::EnumContext::Application::Draw); |
595 | 0 | aApplications.push_back(vcl::EnumContext::Application::Impress); |
596 | 0 | } |
597 | 0 | else if (sApplicationName == "WriterVariants") |
598 | 0 | { |
599 | | // Another special case for all Writer variants. |
600 | 0 | aApplications.push_back(vcl::EnumContext::Application::Writer); |
601 | 0 | aApplications.push_back(vcl::EnumContext::Application::WriterGlobal); |
602 | 0 | aApplications.push_back(vcl::EnumContext::Application::WriterWeb); |
603 | 0 | aApplications.push_back(vcl::EnumContext::Application::WriterXML); |
604 | 0 | aApplications.push_back(vcl::EnumContext::Application::WriterForm); |
605 | 0 | aApplications.push_back(vcl::EnumContext::Application::WriterReport); |
606 | 0 | } |
607 | 0 | else |
608 | 0 | { |
609 | 0 | SAL_WARN("sfx.sidebar", "application name " << sApplicationName << " not recognized"); |
610 | 0 | continue; |
611 | 0 | } |
612 | 0 | } |
613 | 0 | else |
614 | 0 | { |
615 | | // No conversion of the application name necessary. |
616 | 0 | aApplications.push_back(eApplication); |
617 | 0 | } |
618 | | |
619 | | // Setup the actual context enum. |
620 | 0 | const vcl::EnumContext::Context eContext (vcl::EnumContext::GetContextEnum(sContextName)); |
621 | 0 | if (eContext == vcl::EnumContext::Context::Unknown) |
622 | 0 | { |
623 | 0 | SAL_WARN("sfx.sidebar", "context name " << sContextName << " not recognized"); |
624 | 0 | continue; |
625 | 0 | } |
626 | | |
627 | | // Setup the flag that controls whether a deck/pane is |
628 | | // initially visible/expanded. |
629 | 0 | bool bIsInitiallyVisible; |
630 | 0 | if (sInitialState == u"visible") |
631 | 0 | bIsInitiallyVisible = true; |
632 | 0 | else if (sInitialState == u"hidden") |
633 | 0 | bIsInitiallyVisible = false; |
634 | 0 | else |
635 | 0 | { |
636 | 0 | SAL_WARN("sfx.sidebar", "unrecognized state"); |
637 | 0 | continue; |
638 | 0 | } |
639 | | |
640 | | |
641 | | // Add context descriptors. |
642 | 0 | for (auto const& application : aApplications) |
643 | 0 | { |
644 | 0 | if (application != vcl::EnumContext::Application::NONE) |
645 | 0 | { |
646 | 0 | rContextList.AddContextDescription( |
647 | 0 | Context( |
648 | 0 | vcl::EnumContext::GetApplicationName(application), |
649 | 0 | vcl::EnumContext::GetContextName(eContext)), |
650 | 0 | bIsInitiallyVisible, |
651 | 0 | sMenuCommand); |
652 | 0 | } |
653 | 0 | } |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | | void ResourceManager::ReadLegacyAddons (const Reference<frame::XController>& rxController) |
658 | 0 | { |
659 | | // Get module name for given frame. |
660 | 0 | OUString sModuleName (Tools::GetModuleName(rxController)); |
661 | 0 | if (sModuleName.getLength() == 0) |
662 | 0 | return; |
663 | 0 | if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) |
664 | 0 | { |
665 | | // Addons for this application have already been read. |
666 | | // There is nothing more to do. |
667 | 0 | return; |
668 | 0 | } |
669 | | |
670 | | // Mark module as processed. Even when there is an error that |
671 | | // prevents the configuration data from being read, this error |
672 | | // will not be triggered a second time. |
673 | 0 | maProcessedApplications.insert(sModuleName); |
674 | | |
675 | | // Get access to the configuration root node for the application. |
676 | 0 | utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); |
677 | 0 | if (!aLegacyRootNode.isValid()) |
678 | 0 | return; |
679 | | |
680 | | // Process child nodes. |
681 | 0 | std::vector<OUString> aMatchingNodeNames; |
682 | 0 | GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); |
683 | 0 | const sal_Int32 nCount (aMatchingNodeNames.size()); |
684 | 0 | for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) |
685 | 0 | { |
686 | 0 | const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); |
687 | 0 | const utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); |
688 | 0 | if (!aChildNode.isValid()) |
689 | 0 | continue; |
690 | | |
691 | 0 | if ( rsNodeName == "private:resource/toolpanel/DrawingFramework/CustomAnimations" || |
692 | 0 | rsNodeName == "private:resource/toolpanel/DrawingFramework/Layouts" || |
693 | 0 | rsNodeName == "private:resource/toolpanel/DrawingFramework/MasterPages" || |
694 | 0 | rsNodeName == "private:resource/toolpanel/DrawingFramework/SlideTransitions" || |
695 | 0 | rsNodeName == "private:resource/toolpanel/DrawingFramework/TableDesign" ) |
696 | 0 | continue; |
697 | | |
698 | 0 | maDecks.push_back(std::make_shared<DeckDescriptor>()); |
699 | 0 | DeckDescriptor& rDeckDescriptor(*maDecks.back()); |
700 | 0 | rDeckDescriptor.msTitle = getString(aChildNode, u"UIName"_ustr); |
701 | 0 | rDeckDescriptor.msId = rsNodeName; |
702 | 0 | rDeckDescriptor.msIconURL = getString(aChildNode, u"ImageURL"_ustr); |
703 | 0 | rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; |
704 | 0 | rDeckDescriptor.msTitleBarIconURL.clear(); |
705 | 0 | rDeckDescriptor.msHighContrastTitleBarIconURL.clear(); |
706 | 0 | rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; |
707 | 0 | rDeckDescriptor.mbIsEnabled = true; |
708 | 0 | rDeckDescriptor.mnOrderIndex = 100000 + nReadIndex; |
709 | 0 | rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, u"any"_ustr), true, OUString()); |
710 | |
|
711 | 0 | maPanels.push_back(std::make_shared<PanelDescriptor>()); |
712 | 0 | PanelDescriptor& rPanelDescriptor(*maPanels.back()); |
713 | 0 | rPanelDescriptor.msTitle = getString(aChildNode, u"UIName"_ustr); |
714 | 0 | rPanelDescriptor.mbIsTitleBarOptional = true; |
715 | 0 | rPanelDescriptor.msId = rsNodeName; |
716 | 0 | rPanelDescriptor.msDeckId = rsNodeName; |
717 | 0 | rPanelDescriptor.msTitleBarIconURL.clear(); |
718 | 0 | rPanelDescriptor.msHighContrastTitleBarIconURL.clear(); |
719 | 0 | rPanelDescriptor.msImplementationURL = rsNodeName; |
720 | 0 | rPanelDescriptor.mnOrderIndex = 100000 + nReadIndex; |
721 | 0 | rPanelDescriptor.mbShowForReadOnlyDocuments = false; |
722 | 0 | rPanelDescriptor.mbWantsCanvas = false; |
723 | 0 | rPanelDescriptor.mbWantsAWT = true; |
724 | 0 | rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, u"any"_ustr), true, OUString()); |
725 | 0 | } |
726 | 0 | } |
727 | | |
728 | | void ResourceManager::StorePanelExpansionState ( |
729 | | std::u16string_view rsPanelId, |
730 | | const bool bExpansionState, |
731 | | const Context& rContext) |
732 | 0 | { |
733 | 0 | for (auto const& panel : maPanels) |
734 | 0 | { |
735 | 0 | if (panel->msId == rsPanelId) |
736 | 0 | { |
737 | 0 | ContextList::Entry* pEntry(panel->maContextList.GetMatch(rContext)); |
738 | 0 | if (pEntry != nullptr) |
739 | 0 | pEntry->mbIsInitiallyVisible = bExpansionState; |
740 | 0 | } |
741 | 0 | } |
742 | 0 | } |
743 | | |
744 | | utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (const OUString& rsModuleName) |
745 | 0 | { |
746 | 0 | try |
747 | 0 | { |
748 | 0 | const Reference<XComponentContext>& xContext(comphelper::getProcessComponentContext()); |
749 | 0 | const Reference<frame::XModuleManager2> xModuleAccess = frame::ModuleManager::create(xContext); |
750 | 0 | const comphelper::NamedValueCollection aModuleProperties(xModuleAccess->getByName(rsModuleName)); |
751 | 0 | const OUString sWindowStateRef(aModuleProperties.getOrDefault( |
752 | 0 | u"ooSetupFactoryWindowStateConfigRef"_ustr, |
753 | 0 | OUString())); |
754 | |
|
755 | 0 | OUString aPathComposer = "org.openoffice.Office.UI." + sWindowStateRef + |
756 | 0 | "/UIElements/States"; |
757 | |
|
758 | 0 | return utl::OConfigurationTreeRoot(xContext, aPathComposer, false); |
759 | 0 | } |
760 | 0 | catch (const Exception&) |
761 | 0 | { |
762 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.sidebar"); |
763 | 0 | } |
764 | | |
765 | 0 | return utl::OConfigurationTreeRoot(); |
766 | 0 | } |
767 | | |
768 | | void ResourceManager::GetToolPanelNodeNames ( |
769 | | std::vector<OUString>& rMatchingNames, |
770 | | const utl::OConfigurationTreeRoot& aRoot) |
771 | 0 | { |
772 | 0 | const Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); |
773 | 0 | std::copy_if(aChildNodeNames.begin(), aChildNodeNames.end(), std::back_inserter(rMatchingNames), |
774 | 0 | [](const OUString& rChildNodeName) { return rChildNodeName.startsWith( "private:resource/toolpanel/" ); }); |
775 | 0 | } |
776 | | |
777 | | bool ResourceManager::IsDeckEnabled ( |
778 | | std::u16string_view rsDeckId, |
779 | | const Context& rContext, |
780 | | const Reference<frame::XController>& rxController) |
781 | 0 | { |
782 | | |
783 | | // Check if any panel that matches the current context can be |
784 | | // displayed. |
785 | 0 | PanelContextDescriptorContainer aPanelContextDescriptors; |
786 | |
|
787 | 0 | GetMatchingPanels(aPanelContextDescriptors, rContext, rsDeckId, rxController); |
788 | |
|
789 | 0 | for (auto const& panelContextDescriptor : aPanelContextDescriptors) |
790 | 0 | { |
791 | 0 | if (panelContextDescriptor.mbShowForReadOnlyDocuments) |
792 | 0 | return true; |
793 | 0 | } |
794 | 0 | return false; |
795 | 0 | } |
796 | | |
797 | | void ResourceManager::UpdateModel(const css::uno::Reference<css::frame::XModel>& xModel) |
798 | 0 | { |
799 | 0 | for (auto const& deck : maDecks) |
800 | 0 | { |
801 | 0 | if (!deck->mpDeck) |
802 | 0 | continue; |
803 | | |
804 | 0 | const SharedPanelContainer& rContainer = deck->mpDeck->GetPanels(); |
805 | |
|
806 | 0 | for (auto const& elem : rContainer) |
807 | 0 | { |
808 | 0 | css::uno::Reference<css::ui::XUpdateModel> xPanel(elem->GetPanelComponent(), css::uno::UNO_QUERY); |
809 | 0 | if (xPanel.is()) // tdf#108814 interface is optional |
810 | 0 | { |
811 | 0 | xPanel->updateModel(xModel); |
812 | 0 | } |
813 | 0 | } |
814 | 0 | } |
815 | 0 | } |
816 | | |
817 | | void ResourceManager::disposeDecks() |
818 | 0 | { |
819 | 0 | for (auto const& deck : maDecks) |
820 | 0 | { |
821 | 0 | deck->mpDeck.disposeAndClear(); |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | } // end of namespace sfx2::sidebar |
826 | | |
827 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |