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 :
21 : #include "sal/config.h"
22 :
23 : #include <list>
24 : #include <memory>
25 : #include <utility>
26 : #include <vector>
27 : #include <boost/unordered_map.hpp>
28 : #include <boost/shared_ptr.hpp>
29 :
30 : #include "com/sun/star/container/XNameAccess.hpp"
31 : #include "com/sun/star/io/XInputStream.hpp"
32 : #include "com/sun/star/lang/Locale.hpp"
33 : #include "com/sun/star/lang/XMultiServiceFactory.hpp"
34 : #include "com/sun/star/uno/Any.hxx"
35 : #include "com/sun/star/uno/Exception.hpp"
36 : #include "com/sun/star/uno/Reference.hxx"
37 : #include "com/sun/star/uno/RuntimeException.hpp"
38 : #include "com/sun/star/uno/Sequence.hxx"
39 :
40 : #include "comphelper/processfactory.hxx"
41 :
42 : #include "osl/file.hxx"
43 : #include "osl/diagnose.h"
44 :
45 : #include "rtl/bootstrap.hxx"
46 : #include "rtl/string.h"
47 : #include "rtl/textenc.h"
48 : #include "rtl/ustrbuf.hxx"
49 : #include "rtl/ustring.h"
50 : #include "rtl/ustring.hxx"
51 :
52 : #include "sal/types.h"
53 :
54 : #include "tools/stream.hxx"
55 : #include "tools/urlobj.hxx"
56 :
57 : #include "vcl/bitmapex.hxx"
58 : #include "vcl/pngread.hxx"
59 : #include "vcl/settings.hxx"
60 : #include "vcl/svapp.hxx"
61 :
62 : #include "impimagetree.hxx"
63 :
64 : namespace {
65 :
66 3652 : rtl::OUString createPath(
67 : rtl::OUString const & name, sal_Int32 pos, rtl::OUString const & locale)
68 : {
69 3652 : rtl::OUStringBuffer b(name.copy(0, pos + 1));
70 3652 : b.append(locale);
71 3652 : b.append(name.copy(pos));
72 3652 : return b.makeStringAndClear();
73 : }
74 :
75 0 : boost::shared_ptr< SvStream > wrapFile(osl::File & file)
76 : {
77 : // This could use SvInputStream instead if that did not have a broken
78 : // SeekPos implementation for an XInputStream that is not also XSeekable
79 : // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
80 : // l. 593):
81 0 : boost::shared_ptr< SvStream > s(new SvMemoryStream);
82 0 : for (;;) {
83 : void *data[2048];
84 : sal_uInt64 n;
85 0 : file.read(data, 2048, n);
86 0 : s->Write(data, n);
87 0 : if (n < 2048) {
88 : break;
89 : }
90 : }
91 0 : s->Seek(0);
92 0 : return s;
93 : }
94 :
95 0 : void loadFromFile(
96 : osl::File & file,
97 : rtl::OUString const & path, BitmapEx & bitmap)
98 : {
99 0 : boost::shared_ptr< SvStream > s(wrapFile(file));
100 0 : if (path.endsWithAsciiL(RTL_CONSTASCII_STRINGPARAM(".png")))
101 : {
102 0 : vcl::PNGReader aPNGReader( *s );
103 0 : aPNGReader.SetIgnoreGammaChunk( sal_True );
104 0 : bitmap = aPNGReader.Read();
105 : } else {
106 0 : *s >> bitmap;
107 0 : }
108 0 : }
109 :
110 178 : boost::shared_ptr< SvStream > wrapStream(
111 : css::uno::Reference< css::io::XInputStream > const & stream)
112 : {
113 : // This could use SvInputStream instead if that did not have a broken
114 : // SeekPos implementation for an XInputStream that is not also XSeekable
115 : // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
116 : // l. 593):
117 : OSL_ASSERT(stream.is());
118 178 : boost::shared_ptr< SvStream > s(new SvMemoryStream);
119 0 : for (;;) {
120 178 : sal_Int32 const size = 2048;
121 178 : css::uno::Sequence< sal_Int8 > data(size);
122 178 : sal_Int32 n = stream->readBytes(data, size);
123 178 : s->Write(data.getConstArray(), n);
124 178 : if (n < size) {
125 : break;
126 : }
127 178 : }
128 178 : s->Seek(0);
129 178 : return s;
130 : }
131 :
132 178 : void loadFromStream(
133 : css::uno::Reference< css::io::XInputStream > const & stream,
134 : rtl::OUString const & path, BitmapEx & bitmap)
135 : {
136 178 : boost::shared_ptr< SvStream > s(wrapStream(stream));
137 178 : if (path.endsWithAsciiL(RTL_CONSTASCII_STRINGPARAM(".png")))
138 : {
139 178 : vcl::PNGReader aPNGReader( *s );
140 178 : aPNGReader.SetIgnoreGammaChunk( sal_True );
141 178 : bitmap = aPNGReader.Read();
142 : } else {
143 0 : *s >> bitmap;
144 178 : }
145 178 : }
146 :
147 : }
148 :
149 54 : ImplImageTree::ImplImageTree() { m_cacheIcons = true; }
150 :
151 54 : ImplImageTree::~ImplImageTree() {}
152 :
153 416 : bool ImplImageTree::checkStyle(rtl::OUString const & style)
154 : {
155 : bool exists;
156 :
157 : // using cache because setStyle is an expensive operation
158 : // setStyle calls resetPaths => closes any opened zip files with icons, cleans the icon cache, ...
159 416 : if (checkStyleCacheLookup(style, exists)) {
160 46 : return exists;
161 : }
162 :
163 370 : setStyle(style);
164 :
165 370 : exists = false;
166 370 : const rtl::OUString sBrandURLSuffix("_brand");
167 1896 : for (Paths::iterator i(m_paths.begin()); i != m_paths.end() && !exists; ++i) {
168 1526 : ::rtl::OUString aURL = i->first;
169 1526 : sal_Int32 nFromIndex = aURL.getLength() - sBrandURLSuffix.getLength();
170 : // skip brand-specific icon themes; they are incomplete and thus not useful for this check
171 1526 : if (nFromIndex < 0 || !aURL.match(sBrandURLSuffix, nFromIndex)) {
172 786 : osl::File aZip(aURL + ".zip");
173 786 : if (aZip.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) {
174 2 : aZip.close();
175 2 : exists = true;
176 : }
177 :
178 786 : osl::Directory aLookaside(aURL);
179 786 : if (aLookaside.open() == ::osl::FileBase::E_None) {
180 0 : aLookaside.close();
181 0 : exists = true;
182 0 : m_cacheIcons = false;
183 : } else {
184 786 : m_cacheIcons = true;
185 786 : }
186 : }
187 1526 : }
188 370 : m_checkStyleCache[style] = exists;
189 370 : return exists;
190 : }
191 :
192 1830 : bool ImplImageTree::loadImage(
193 : rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap,
194 : bool localized, bool loadMissing )
195 : {
196 1830 : bool found = false;
197 : try {
198 1830 : found = doLoadImage(name, style, bitmap, localized);
199 0 : } catch (css::uno::RuntimeException &) {
200 0 : if (!loadMissing)
201 0 : throw;
202 : }
203 1830 : if (found || !loadMissing)
204 1830 : return found;
205 :
206 : OSL_TRACE(
207 : "ImplImageTree::loadImage exception couldn't load \"%s\", fetching default image",
208 : rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr());
209 0 : return loadDefaultImage(style, bitmap);
210 : }
211 :
212 90 : bool ImplImageTree::loadDefaultImage(
213 : rtl::OUString const & style,
214 : BitmapEx& bitmap)
215 : {
216 : return doLoadImage(
217 : rtl::OUString("res/grafikde.png"),
218 90 : style, bitmap, false);
219 : }
220 :
221 :
222 1920 : bool ImplImageTree::doLoadImage(
223 : rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap,
224 : bool localized)
225 : {
226 1920 : setStyle(style);
227 1920 : if (m_cacheIcons && iconCacheLookup(name, localized, bitmap)) {
228 4 : return true;
229 : }
230 1916 : if (!bitmap.IsEmpty()) {
231 0 : bitmap.SetEmpty();
232 : }
233 1916 : std::vector< rtl::OUString > paths;
234 1916 : paths.push_back(name);
235 1916 : if (localized) {
236 1826 : sal_Int32 pos = name.lastIndexOf('/');
237 1826 : if (pos != -1) {
238 : /* FIXME-BCP47: this needs to be changed for language tags! */
239 : css::lang::Locale const & loc =
240 1826 : Application::GetSettings().GetUILanguageTag().getLocale();
241 1826 : paths.push_back(createPath(name, pos, loc.Language));
242 1826 : if (!loc.Country.isEmpty()) {
243 1826 : rtl::OUStringBuffer b(loc.Language);
244 1826 : b.append(sal_Unicode('-'));
245 1826 : b.append(loc.Country);
246 1826 : rtl::OUString p(createPath(name, pos, b.makeStringAndClear()));
247 1826 : paths.push_back(p);
248 1826 : if (!loc.Variant.isEmpty()) {
249 0 : b.append(p);
250 0 : b.append(sal_Unicode('-'));
251 0 : b.append(loc.Variant);
252 : paths.push_back(
253 0 : createPath(name, pos, b.makeStringAndClear()));
254 1826 : }
255 : }
256 : }
257 : }
258 1916 : bool found = false;
259 : try {
260 1916 : found = find(paths, bitmap);
261 0 : } catch (css::uno::RuntimeException &) {
262 0 : throw;
263 0 : } catch (const css::uno::Exception & e) {
264 : OSL_TRACE(
265 : "ImplImageTree::loadImage exception \"%s\"",
266 : rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
267 : }
268 1916 : if (m_cacheIcons && found) {
269 178 : m_iconCache[name.intern()] = std::make_pair(localized, bitmap);
270 : }
271 1916 : return found;
272 : }
273 :
274 8 : void ImplImageTree::shutDown() {
275 8 : m_style = rtl::OUString();
276 : // for safety; empty m_style means "not initialized"
277 8 : m_paths.clear();
278 8 : m_iconCache.clear();
279 8 : m_checkStyleCache.clear();
280 8 : }
281 :
282 2290 : void ImplImageTree::setStyle(rtl::OUString const & style) {
283 : OSL_ASSERT(!style.isEmpty()); // empty m_style means "not initialized"
284 2290 : if (style != m_style) {
285 416 : m_style = style;
286 416 : resetPaths();
287 416 : m_iconCache.clear();
288 : }
289 2290 : }
290 :
291 416 : void ImplImageTree::resetPaths() {
292 416 : m_paths.clear();
293 : {
294 : rtl::OUString url(
295 416 : "$BRAND_BASE_DIR/program/edition/images");
296 416 : rtl::Bootstrap::expandMacros(url);
297 416 : INetURLObject u(url);
298 : OSL_ASSERT(!u.HasError());
299 : m_paths.push_back(
300 : std::make_pair(
301 : u.GetMainURL(INetURLObject::NO_DECODE),
302 416 : css::uno::Reference< css::container::XNameAccess >()));
303 : }
304 : {
305 : rtl::OUString url(
306 416 : "$BRAND_BASE_DIR/share/config");
307 416 : rtl::Bootstrap::expandMacros(url);
308 416 : INetURLObject u(url);
309 : OSL_ASSERT(!u.HasError());
310 416 : rtl::OUStringBuffer b;
311 416 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
312 416 : b.append(m_style);
313 416 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("_brand"));
314 416 : bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL);
315 : OSL_ASSERT(ok); (void) ok;
316 : m_paths.push_back(
317 : std::make_pair(
318 : u.GetMainURL(INetURLObject::NO_DECODE),
319 416 : css::uno::Reference< css::container::XNameAccess >()));
320 : }
321 : {
322 416 : rtl::OUString url( "$BRAND_BASE_DIR/share/config/images_brand");
323 416 : rtl::Bootstrap::expandMacros(url);
324 : m_paths.push_back(
325 : std::make_pair(
326 416 : url, css::uno::Reference< css::container::XNameAccess >()));
327 : }
328 : {
329 : rtl::OUString url(
330 416 : "$BRAND_BASE_DIR/share/config");
331 416 : rtl::Bootstrap::expandMacros(url);
332 416 : INetURLObject u(url);
333 : OSL_ASSERT(!u.HasError());
334 416 : rtl::OUStringBuffer b;
335 416 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
336 416 : b.append(m_style);
337 416 : bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL);
338 : OSL_ASSERT(ok); (void) ok;
339 : m_paths.push_back(
340 : std::make_pair(
341 : u.GetMainURL(INetURLObject::NO_DECODE),
342 416 : css::uno::Reference< css::container::XNameAccess >()));
343 : }
344 416 : if ( m_style == "default" )
345 : {
346 46 : rtl::OUString url( "$BRAND_BASE_DIR/share/config/images");
347 46 : rtl::Bootstrap::expandMacros(url);
348 : m_paths.push_back(
349 : std::make_pair(
350 46 : url, css::uno::Reference< css::container::XNameAccess >()));
351 : }
352 416 : }
353 :
354 416 : bool ImplImageTree::checkStyleCacheLookup(
355 : rtl::OUString const & style, bool &exists)
356 : {
357 416 : CheckStyleCache::iterator i(m_checkStyleCache.find(style));
358 416 : if (i != m_checkStyleCache.end()) {
359 46 : exists = i->second;
360 46 : return true;
361 : } else {
362 370 : return false;
363 : }
364 : }
365 :
366 1920 : bool ImplImageTree::iconCacheLookup(
367 : rtl::OUString const & name, bool localized, BitmapEx & bitmap)
368 : {
369 1920 : IconCache::iterator i(m_iconCache.find(name));
370 1920 : if (i != m_iconCache.end() && i->second.first == localized) {
371 4 : bitmap = i->second.second;
372 4 : return true;
373 : } else {
374 1916 : return false;
375 : }
376 : }
377 :
378 1916 : bool ImplImageTree::find(
379 : std::vector< rtl::OUString > const & paths, BitmapEx & bitmap)
380 : {
381 1916 : if (!m_cacheIcons) {
382 0 : for (Paths::iterator i(m_paths.begin()); i != m_paths.end(); ++i) {
383 0 : for (std::vector< rtl::OUString >::const_reverse_iterator j(
384 0 : paths.rbegin());
385 0 : j != paths.rend(); ++j)
386 : {
387 0 : osl::File file(i->first + "/" + *j);
388 0 : if (file.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) {
389 0 : loadFromFile(file, *j, bitmap);
390 0 : file.close();
391 0 : return true;
392 : }
393 0 : }
394 : }
395 : }
396 :
397 4022 : for (Paths::iterator i(m_paths.begin()); i != m_paths.end();) {
398 368 : if (!i->second.is()) {
399 192 : css::uno::Sequence< css::uno::Any > args(1);
400 192 : args[0] <<= i->first + ".zip";
401 : try {
402 2 : i->second.set(
403 384 : comphelper::getProcessServiceFactory()->createInstanceWithArguments(
404 : rtl::OUString( "com.sun.star.packages.zip.ZipFileAccess"),
405 192 : args),
406 194 : css::uno::UNO_QUERY_THROW);
407 0 : } catch (css::uno::RuntimeException &) {
408 0 : throw;
409 380 : } catch (const css::uno::Exception & e) {
410 : OSL_TRACE(
411 : "ImplImageTree::find exception \"%s\"",
412 : rtl::OUStringToOString(
413 : e.Message, RTL_TEXTENCODING_UTF8).getStr());
414 190 : i = m_paths.erase(i);
415 190 : continue;
416 192 : }
417 : }
418 1602 : for (std::vector< rtl::OUString >::const_reverse_iterator j(
419 178 : paths.rbegin());
420 1068 : j != paths.rend(); ++j)
421 : {
422 534 : if (i->second->hasByName(*j)) {
423 178 : css::uno::Reference< css::io::XInputStream > s;
424 178 : bool ok = i->second->getByName(*j) >>= s;
425 : OSL_ASSERT(ok); (void) ok;
426 178 : loadFromStream(s, *j, bitmap);
427 178 : return true;
428 : }
429 : }
430 0 : ++i;
431 : }
432 1738 : return false;
433 : }
434 :
435 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|