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 1486 : rtl::OUString createPath(
67 : rtl::OUString const & name, sal_Int32 pos, rtl::OUString const & locale)
68 : {
69 1486 : rtl::OUStringBuffer b(name.copy(0, pos + 1));
70 1486 : b.append(locale);
71 1486 : b.append(name.copy(pos));
72 1486 : 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 0 : 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 0 : boost::shared_ptr< SvStream > s(new SvMemoryStream);
119 0 : for (;;) {
120 0 : sal_Int32 const size = 2048;
121 0 : css::uno::Sequence< sal_Int8 > data(size);
122 0 : sal_Int32 n = stream->readBytes(data, size);
123 0 : s->Write(data.getConstArray(), n);
124 0 : if (n < size) {
125 : break;
126 : }
127 0 : }
128 0 : s->Seek(0);
129 0 : return s;
130 : }
131 :
132 0 : void loadFromStream(
133 : css::uno::Reference< css::io::XInputStream > const & stream,
134 : rtl::OUString const & path, BitmapEx & bitmap)
135 : {
136 0 : boost::shared_ptr< SvStream > s(wrapStream(stream));
137 0 : if (path.endsWithAsciiL(RTL_CONSTASCII_STRINGPARAM(".png")))
138 : {
139 0 : vcl::PNGReader aPNGReader( *s );
140 0 : aPNGReader.SetIgnoreGammaChunk( sal_True );
141 0 : bitmap = aPNGReader.Read();
142 : } else {
143 0 : *s >> bitmap;
144 0 : }
145 0 : }
146 :
147 : }
148 :
149 10 : ImplImageTree::ImplImageTree() { m_cacheIcons = true; }
150 :
151 10 : ImplImageTree::~ImplImageTree() {}
152 :
153 90 : 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 90 : if (checkStyleCacheLookup(style, exists)) {
160 10 : return exists;
161 : }
162 :
163 80 : setStyle(style);
164 :
165 80 : exists = false;
166 80 : const rtl::OUString sBrandURLSuffix("_brand");
167 410 : for (Paths::iterator i(m_paths.begin()); i != m_paths.end() && !exists; ++i) {
168 330 : ::rtl::OUString aURL = i->first;
169 330 : sal_Int32 nFromIndex = aURL.getLength() - sBrandURLSuffix.getLength();
170 : // skip brand-specific icon themes; they are incomplete and thus not useful for this check
171 330 : if (nFromIndex < 0 || !aURL.match(sBrandURLSuffix, nFromIndex)) {
172 170 : osl::File aZip(aURL + ".zip");
173 170 : if (aZip.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) {
174 0 : aZip.close();
175 0 : exists = true;
176 : }
177 :
178 170 : osl::Directory aLookaside(aURL);
179 170 : if (aLookaside.open() == ::osl::FileBase::E_None) {
180 0 : aLookaside.close();
181 0 : exists = true;
182 0 : m_cacheIcons = false;
183 : } else {
184 170 : m_cacheIcons = true;
185 170 : }
186 : }
187 330 : }
188 80 : m_checkStyleCache[style] = exists;
189 80 : return exists;
190 : }
191 :
192 743 : bool ImplImageTree::loadImage(
193 : rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap,
194 : bool localized, bool loadMissing )
195 : {
196 743 : bool found = false;
197 : try {
198 743 : found = doLoadImage(name, style, bitmap, localized);
199 0 : } catch (css::uno::RuntimeException &) {
200 0 : if (!loadMissing)
201 0 : throw;
202 : }
203 743 : if (found || !loadMissing)
204 743 : 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 0 : bool ImplImageTree::loadDefaultImage(
213 : rtl::OUString const & style,
214 : BitmapEx& bitmap)
215 : {
216 : return doLoadImage(
217 : rtl::OUString("res/grafikde.png"),
218 0 : style, bitmap, false);
219 : }
220 :
221 :
222 743 : bool ImplImageTree::doLoadImage(
223 : rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap,
224 : bool localized)
225 : {
226 743 : setStyle(style);
227 743 : if (m_cacheIcons && iconCacheLookup(name, localized, bitmap)) {
228 0 : return true;
229 : }
230 743 : if (!bitmap.IsEmpty()) {
231 0 : bitmap.SetEmpty();
232 : }
233 743 : std::vector< rtl::OUString > paths;
234 743 : paths.push_back(name);
235 743 : if (localized) {
236 743 : sal_Int32 pos = name.lastIndexOf('/');
237 743 : if (pos != -1) {
238 : /* FIXME-BCP47: this needs to be changed for language tags! */
239 : css::lang::Locale const & loc =
240 743 : Application::GetSettings().GetUILanguageTag().getLocale();
241 743 : paths.push_back(createPath(name, pos, loc.Language));
242 743 : if (!loc.Country.isEmpty()) {
243 743 : rtl::OUStringBuffer b(loc.Language);
244 743 : b.append(sal_Unicode('-'));
245 743 : b.append(loc.Country);
246 743 : rtl::OUString p(createPath(name, pos, b.makeStringAndClear()));
247 743 : paths.push_back(p);
248 743 : 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 743 : }
255 : }
256 : }
257 : }
258 743 : bool found = false;
259 : try {
260 743 : 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 743 : if (m_cacheIcons && found) {
269 0 : m_iconCache[name.intern()] = std::make_pair(localized, bitmap);
270 : }
271 743 : return found;
272 : }
273 :
274 0 : void ImplImageTree::shutDown() {
275 0 : m_style = rtl::OUString();
276 : // for safety; empty m_style means "not initialized"
277 0 : m_paths.clear();
278 0 : m_iconCache.clear();
279 0 : m_checkStyleCache.clear();
280 0 : }
281 :
282 823 : void ImplImageTree::setStyle(rtl::OUString const & style) {
283 : OSL_ASSERT(!style.isEmpty()); // empty m_style means "not initialized"
284 823 : if (style != m_style) {
285 90 : m_style = style;
286 90 : resetPaths();
287 90 : m_iconCache.clear();
288 : }
289 823 : }
290 :
291 90 : void ImplImageTree::resetPaths() {
292 90 : m_paths.clear();
293 : {
294 : rtl::OUString url(
295 90 : "$BRAND_BASE_DIR/program/edition/images");
296 90 : rtl::Bootstrap::expandMacros(url);
297 90 : INetURLObject u(url);
298 : OSL_ASSERT(!u.HasError());
299 : m_paths.push_back(
300 : std::make_pair(
301 : u.GetMainURL(INetURLObject::NO_DECODE),
302 90 : css::uno::Reference< css::container::XNameAccess >()));
303 : }
304 : {
305 : rtl::OUString url(
306 90 : "$BRAND_BASE_DIR/share/config");
307 90 : rtl::Bootstrap::expandMacros(url);
308 90 : INetURLObject u(url);
309 : OSL_ASSERT(!u.HasError());
310 90 : rtl::OUStringBuffer b;
311 90 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
312 90 : b.append(m_style);
313 90 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("_brand"));
314 90 : 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 90 : css::uno::Reference< css::container::XNameAccess >()));
320 : }
321 : {
322 90 : rtl::OUString url( "$BRAND_BASE_DIR/share/config/images_brand");
323 90 : rtl::Bootstrap::expandMacros(url);
324 : m_paths.push_back(
325 : std::make_pair(
326 90 : url, css::uno::Reference< css::container::XNameAccess >()));
327 : }
328 : {
329 : rtl::OUString url(
330 90 : "$BRAND_BASE_DIR/share/config");
331 90 : rtl::Bootstrap::expandMacros(url);
332 90 : INetURLObject u(url);
333 : OSL_ASSERT(!u.HasError());
334 90 : rtl::OUStringBuffer b;
335 90 : b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
336 90 : b.append(m_style);
337 90 : 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 90 : css::uno::Reference< css::container::XNameAccess >()));
343 : }
344 90 : if ( m_style == "default" )
345 : {
346 10 : rtl::OUString url( "$BRAND_BASE_DIR/share/config/images");
347 10 : rtl::Bootstrap::expandMacros(url);
348 : m_paths.push_back(
349 : std::make_pair(
350 10 : url, css::uno::Reference< css::container::XNameAccess >()));
351 : }
352 90 : }
353 :
354 90 : bool ImplImageTree::checkStyleCacheLookup(
355 : rtl::OUString const & style, bool &exists)
356 : {
357 90 : CheckStyleCache::iterator i(m_checkStyleCache.find(style));
358 90 : if (i != m_checkStyleCache.end()) {
359 10 : exists = i->second;
360 10 : return true;
361 : } else {
362 80 : return false;
363 : }
364 : }
365 :
366 743 : bool ImplImageTree::iconCacheLookup(
367 : rtl::OUString const & name, bool localized, BitmapEx & bitmap)
368 : {
369 743 : IconCache::iterator i(m_iconCache.find(name));
370 743 : if (i != m_iconCache.end() && i->second.first == localized) {
371 0 : bitmap = i->second.second;
372 0 : return true;
373 : } else {
374 743 : return false;
375 : }
376 : }
377 :
378 743 : bool ImplImageTree::find(
379 : std::vector< rtl::OUString > const & paths, BitmapEx & bitmap)
380 : {
381 743 : 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 1526 : for (Paths::iterator i(m_paths.begin()); i != m_paths.end();) {
398 40 : if (!i->second.is()) {
399 40 : css::uno::Sequence< css::uno::Any > args(1);
400 40 : args[0] <<= i->first + ".zip";
401 : try {
402 0 : i->second.set(
403 80 : comphelper::getProcessServiceFactory()->createInstanceWithArguments(
404 : rtl::OUString( "com.sun.star.packages.zip.ZipFileAccess"),
405 40 : args),
406 40 : css::uno::UNO_QUERY_THROW);
407 0 : } catch (css::uno::RuntimeException &) {
408 0 : throw;
409 80 : } 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 40 : i = m_paths.erase(i);
415 40 : continue;
416 40 : }
417 : }
418 0 : for (std::vector< rtl::OUString >::const_reverse_iterator j(
419 0 : paths.rbegin());
420 0 : j != paths.rend(); ++j)
421 : {
422 0 : if (i->second->hasByName(*j)) {
423 0 : css::uno::Reference< css::io::XInputStream > s;
424 0 : bool ok = i->second->getByName(*j) >>= s;
425 : OSL_ASSERT(ok); (void) ok;
426 0 : loadFromStream(s, *j, bitmap);
427 0 : return true;
428 : }
429 : }
430 0 : ++i;
431 : }
432 743 : return false;
433 : }
434 :
435 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|