Line data Source code
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 <config_folders.h>
21 :
22 : #include "sal/config.h"
23 :
24 : #include "com/sun/star/container/XNameAccess.hpp"
25 : #include "com/sun/star/io/XInputStream.hpp"
26 : #include "com/sun/star/lang/Locale.hpp"
27 : #include "com/sun/star/lang/XMultiServiceFactory.hpp"
28 : #include "com/sun/star/packages/zip/ZipFileAccess.hpp"
29 : #include "com/sun/star/uno/Any.hxx"
30 : #include "com/sun/star/uno/Exception.hpp"
31 : #include "com/sun/star/uno/RuntimeException.hpp"
32 : #include "com/sun/star/uno/Sequence.hxx"
33 : #include "comphelper/processfactory.hxx"
34 : #include "osl/file.hxx"
35 : #include "osl/diagnose.h"
36 : #include "rtl/bootstrap.hxx"
37 :
38 : #include "tools/stream.hxx"
39 : #include "tools/urlobj.hxx"
40 : #include "vcl/bitmapex.hxx"
41 : #include <vcl/dibtools.hxx>
42 : #include "vcl/pngread.hxx"
43 : #include "vcl/settings.hxx"
44 : #include "vcl/svapp.hxx"
45 : #include "impimagetree.hxx"
46 : #include <vcldemo-debug.hxx>
47 :
48 : using namespace css;
49 :
50 : namespace {
51 :
52 52386 : static OUString createPath(OUString const & name, sal_Int32 pos, OUString const & locale)
53 : {
54 52386 : return name.copy(0, pos + 1) + locale + name.copy(pos);
55 : }
56 :
57 11026 : static std::shared_ptr<SvStream> wrapStream(css::uno::Reference< css::io::XInputStream > const & stream)
58 : {
59 : // This could use SvInputStream instead if that did not have a broken
60 : // SeekPos implementation for an XInputStream that is not also XSeekable
61 : // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
62 : // l. 593):
63 : OSL_ASSERT(stream.is());
64 11026 : std::shared_ptr<SvStream> s(std::make_shared<SvMemoryStream>());
65 : for (;;)
66 : {
67 12389 : sal_Int32 const size = 2048;
68 12389 : css::uno::Sequence< sal_Int8 > data(size);
69 12389 : sal_Int32 n = stream->readBytes(data, size);
70 12389 : s->Write(data.getConstArray(), n);
71 12389 : if (n < size)
72 11026 : break;
73 1363 : }
74 11026 : s->Seek(0);
75 11026 : return s;
76 : }
77 :
78 10804 : static void loadImageFromStream(std::shared_ptr<SvStream> xStream, OUString const & rPath, BitmapEx & rBitmap)
79 : {
80 10804 : if (rPath.endsWith(".png"))
81 : {
82 10802 : vcl::PNGReader aPNGReader(*xStream);
83 10802 : aPNGReader.SetIgnoreGammaChunk( true );
84 10802 : rBitmap = aPNGReader.Read();
85 : }
86 : else
87 : {
88 2 : ReadDIBBitmapEx(rBitmap, *xStream);
89 : }
90 10804 : }
91 :
92 : }
93 :
94 244 : ImplImageTree::ImplImageTree()
95 : {
96 244 : }
97 :
98 244 : ImplImageTree::~ImplImageTree()
99 : {
100 244 : }
101 :
102 18767 : OUString ImplImageTree::fallbackStyle(const OUString &style)
103 : {
104 18767 : if (style == "galaxy")
105 19 : return OUString();
106 18748 : else if (style == "industrial")
107 0 : return OUString("galaxy");
108 18748 : else if (style == "tango")
109 18748 : return OUString("galaxy");
110 0 : else if (style == "breeze")
111 0 : return OUString("galaxy");
112 0 : else if (style == "sifr")
113 0 : return OUString("breeze");
114 :
115 0 : return OUString("tango");
116 : }
117 :
118 44309 : bool ImplImageTree::loadImage(OUString const & name, OUString const & style, BitmapEx & bitmap,
119 : bool localized, bool loadMissing)
120 : {
121 44309 : OUString aStyle(style);
122 107385 : while (!aStyle.isEmpty())
123 : {
124 : try {
125 63057 : if (doLoadImage(name, aStyle, bitmap, localized))
126 44290 : return true;
127 : }
128 0 : catch (css::uno::RuntimeException &) {}
129 :
130 18767 : aStyle = fallbackStyle(aStyle);
131 : }
132 :
133 19 : if (!loadMissing)
134 19 : return false;
135 :
136 : SAL_INFO("vcl", "ImplImageTree::loadImage couldn't load \"" << name << "\", fetching default image");
137 :
138 0 : return loadDefaultImage(style, bitmap);
139 : }
140 :
141 339 : bool ImplImageTree::loadDefaultImage(OUString const & style, BitmapEx& bitmap)
142 : {
143 : return doLoadImage(
144 : OUString("res/grafikde.png"),
145 339 : style, bitmap, false);
146 : }
147 :
148 63396 : bool ImplImageTree::doLoadImage(OUString const & name, OUString const & style, BitmapEx & bitmap,
149 : bool localized)
150 : {
151 63396 : setStyle(style);
152 63396 : if (iconCacheLookup(name, localized, bitmap))
153 33486 : return true;
154 :
155 29910 : if (!bitmap.IsEmpty())
156 0 : bitmap.SetEmpty();
157 :
158 29910 : std::vector< OUString > paths;
159 29910 : paths.push_back(getRealImageName(name));
160 :
161 29910 : if (localized)
162 : {
163 26193 : sal_Int32 pos = name.lastIndexOf('/');
164 26193 : if (pos != -1)
165 : {
166 : // findImage() uses a reverse iterator, so push in reverse order.
167 26193 : std::vector< OUString > aFallbacks( Application::GetSettings().GetUILanguageTag().getFallbackStrings(true));
168 235737 : for (std::vector< OUString >::reverse_iterator it( aFallbacks.rbegin());
169 157158 : it != aFallbacks.rend(); ++it)
170 : {
171 52386 : paths.push_back( getRealImageName( createPath(name, pos, *it) ) );
172 26193 : }
173 : }
174 : }
175 :
176 29910 : bool found = false;
177 : try {
178 29910 : found = findImage(paths, bitmap);
179 0 : } catch (css::uno::RuntimeException &) {
180 0 : throw;
181 0 : } catch (const css::uno::Exception & e) {
182 : SAL_INFO("vcl", "ImplImageTree::doLoadImage exception " << e.Message);
183 : }
184 :
185 29910 : if (found)
186 10804 : maIconSet[maCurrentStyle].maIconCache[name] = std::make_pair(localized, bitmap);
187 :
188 29910 : return found;
189 : }
190 :
191 242 : void ImplImageTree::shutDown()
192 : {
193 242 : maCurrentStyle.clear();
194 460 : for (StyleIconSet::iterator it = maIconSet.begin(); it != maIconSet.end(); ++it)
195 : {
196 218 : it->second.maIconCache.clear();
197 218 : it->second.maLinkHash.clear();
198 : }
199 242 : }
200 :
201 63396 : void ImplImageTree::setStyle(OUString const & style)
202 : {
203 : assert(!style.isEmpty());
204 63396 : if (style != maCurrentStyle)
205 : {
206 37521 : maCurrentStyle = style;
207 37521 : createStyle();
208 : }
209 63396 : }
210 :
211 37521 : void ImplImageTree::createStyle()
212 : {
213 37521 : if (maIconSet.find(maCurrentStyle) != maIconSet.end())
214 74820 : return;
215 :
216 222 : OUString url( "$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/config/" );
217 222 : rtl::Bootstrap::expandMacros(url);
218 222 : if (maCurrentStyle != "default")
219 : {
220 222 : INetURLObject u(url);
221 : OSL_ASSERT(!u.HasError());
222 222 : bool ok = u.Append("images_" + maCurrentStyle, INetURLObject::ENCODE_ALL);
223 : OSL_ASSERT(ok); (void) ok;
224 222 : url = u.GetMainURL(INetURLObject::NO_DECODE);
225 : }
226 : else
227 0 : url += "images";
228 :
229 222 : maIconSet[maCurrentStyle] = IconSet(url);
230 :
231 222 : loadImageLinks();
232 : }
233 :
234 63396 : bool ImplImageTree::iconCacheLookup(OUString const & name, bool localized, BitmapEx & bitmap)
235 : {
236 63396 : IconCache &rIconCache = maIconSet[maCurrentStyle].maIconCache;
237 :
238 63396 : IconCache::iterator i(rIconCache.find(getRealImageName(name)));
239 63396 : if (i != rIconCache.end() && i->second.first == localized)
240 : {
241 33486 : bitmap = i->second.second;
242 33486 : return true;
243 : }
244 29910 : return false;
245 : }
246 :
247 29910 : bool ImplImageTree::findImage(std::vector<OUString> const & paths, BitmapEx & bitmap)
248 : {
249 29910 : if (!checkPathAccess())
250 0 : return false;
251 :
252 29910 : const uno::Reference<container::XNameAccess> &rNameAccess = maIconSet[maCurrentStyle].maNameAccess;
253 :
254 101402 : for (std::vector<OUString>::const_reverse_iterator j(paths.rbegin()); j != paths.rend(); ++j)
255 : {
256 82296 : if (rNameAccess->hasByName(*j))
257 : {
258 10804 : css::uno::Reference< css::io::XInputStream > s;
259 10804 : bool ok = rNameAccess->getByName(*j) >>= s;
260 : assert(ok);
261 : (void)ok; // prevent unused warning in release build
262 :
263 10804 : loadImageFromStream( wrapStream(s), *j, bitmap );
264 10804 : return true;
265 : }
266 : }
267 19106 : return false;
268 : }
269 :
270 222 : void ImplImageTree::loadImageLinks()
271 : {
272 222 : const OUString aLinkFilename("links.txt");
273 :
274 222 : if (!checkPathAccess())
275 0 : return;
276 :
277 222 : const uno::Reference<container::XNameAccess> &rNameAccess = maIconSet[maCurrentStyle].maNameAccess;
278 :
279 222 : if (rNameAccess->hasByName(aLinkFilename))
280 : {
281 222 : css::uno::Reference< css::io::XInputStream > s;
282 222 : bool ok = rNameAccess->getByName(aLinkFilename) >>= s;
283 : assert(ok);
284 : (void)ok; // prevent unused warning in release build
285 :
286 222 : parseLinkFile( wrapStream(s) );
287 222 : return;
288 0 : }
289 : }
290 :
291 222 : void ImplImageTree::parseLinkFile(std::shared_ptr<SvStream> xStream)
292 : {
293 222 : OString aLine;
294 444 : OUString aLink, aOriginal;
295 222 : int nLineNo = 0;
296 65268 : while (xStream->ReadLine(aLine))
297 : {
298 64824 : ++nLineNo;
299 64824 : if ( aLine.isEmpty() )
300 0 : continue;
301 :
302 64824 : sal_Int32 nIndex = 0;
303 64824 : aLink = OStringToOUString( aLine.getToken(0, ' ', nIndex), RTL_TEXTENCODING_UTF8 );
304 64824 : aOriginal = OStringToOUString( aLine.getToken(0, ' ', nIndex), RTL_TEXTENCODING_UTF8 );
305 :
306 : // skip comments, or incomplete entries
307 64824 : if (aLink.isEmpty() || aLink[0] == '#' || aOriginal.isEmpty())
308 : {
309 0 : if (aLink.isEmpty() || aOriginal.isEmpty())
310 : SAL_WARN("vcl", "ImplImageTree::parseLinkFile: icon links.txt parse error, incomplete link at line " << nLineNo);
311 0 : continue;
312 : }
313 :
314 64824 : maIconSet[maCurrentStyle].maLinkHash[aLink] = aOriginal;
315 222 : }
316 222 : }
317 :
318 145692 : OUString const & ImplImageTree::getRealImageName(OUString const & name)
319 : {
320 145692 : IconLinkHash &rLinkHash = maIconSet[maCurrentStyle].maLinkHash;
321 :
322 145692 : IconLinkHash::iterator it(rLinkHash.find(name));
323 145692 : if (it == rLinkHash.end())
324 125063 : return name;
325 :
326 20629 : return it->second;
327 : }
328 :
329 30132 : bool ImplImageTree::checkPathAccess()
330 : {
331 30132 : uno::Reference<container::XNameAccess> &rNameAccess = maIconSet[maCurrentStyle].maNameAccess;
332 30132 : if (rNameAccess.is())
333 29910 : return true;
334 :
335 : try {
336 222 : rNameAccess = css::packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), maIconSet[maCurrentStyle].maURL + ".zip");
337 : }
338 0 : catch (const css::uno::RuntimeException &) {
339 0 : throw;
340 : }
341 0 : catch (const css::uno::Exception & e) {
342 : SAL_INFO("vcl", "ImplImageTree::zip file location exception " << e.Message << " for " << maIconSet[maCurrentStyle].maURL);
343 0 : return false;
344 : }
345 222 : return rNameAccess.is();
346 : }
347 :
348 0 : css::uno::Reference<css::container::XNameAccess> ImplImageTree::getNameAccess()
349 : {
350 0 : checkPathAccess();
351 0 : return maIconSet[maCurrentStyle].maNameAccess;
352 : }
353 :
354 : /// Recursively dump all names ...
355 0 : css::uno::Sequence<OUString> ImageTree_getAllImageNames()
356 : {
357 0 : static ImplImageTreeSingletonRef aImageTree;
358 :
359 0 : css::uno::Reference<css::container::XNameAccess> xRef(aImageTree->getNameAccess());
360 :
361 0 : return xRef->getElementNames();
362 : }
363 :
364 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|