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 <boost/shared_ptr.hpp>
24 :
25 : #include "com/sun/star/container/XNameAccess.hpp"
26 : #include "com/sun/star/io/XInputStream.hpp"
27 : #include "com/sun/star/lang/Locale.hpp"
28 : #include "com/sun/star/lang/XMultiServiceFactory.hpp"
29 : #include "com/sun/star/packages/zip/ZipFileAccess.hpp"
30 : #include "com/sun/star/uno/Any.hxx"
31 : #include "com/sun/star/uno/Exception.hpp"
32 : #include "com/sun/star/uno/RuntimeException.hpp"
33 : #include "com/sun/star/uno/Sequence.hxx"
34 : #include "comphelper/processfactory.hxx"
35 : #include "osl/file.hxx"
36 : #include "osl/diagnose.h"
37 : #include "rtl/bootstrap.hxx"
38 :
39 : #include "tools/stream.hxx"
40 : #include "tools/urlobj.hxx"
41 : #include "vcl/bitmapex.hxx"
42 : #include <vcl/dibtools.hxx>
43 : #include "vcl/pngread.hxx"
44 : #include "vcl/settings.hxx"
45 : #include "vcl/svapp.hxx"
46 : #include "impimagetree.hxx"
47 : #include <vcl/dibtools.hxx>
48 :
49 : namespace {
50 :
51 9924 : static OUString createPath(
52 : OUString const & name, sal_Int32 pos, OUString const & locale)
53 : {
54 9924 : return name.copy(0, pos + 1) + locale + name.copy(pos);
55 : }
56 :
57 0 : static boost::shared_ptr< SvStream > wrapFile(osl::File & file)
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 0 : boost::shared_ptr< SvStream > s(new SvMemoryStream);
64 : for (;;) {
65 : void *data[2048];
66 : sal_uInt64 n;
67 0 : file.read(data, 2048, n);
68 0 : s->Write(data, n);
69 0 : if (n < 2048) {
70 0 : break;
71 : }
72 0 : }
73 0 : s->Seek(0);
74 0 : return s;
75 : }
76 :
77 3456 : static boost::shared_ptr< SvStream > wrapStream(
78 : css::uno::Reference< css::io::XInputStream > const & stream)
79 : {
80 : // This could use SvInputStream instead if that did not have a broken
81 : // SeekPos implementation for an XInputStream that is not also XSeekable
82 : // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
83 : // l. 593):
84 : OSL_ASSERT(stream.is());
85 3456 : boost::shared_ptr< SvStream > s(new SvMemoryStream);
86 : for (;;) {
87 3748 : sal_Int32 const size = 2048;
88 3748 : css::uno::Sequence< sal_Int8 > data(size);
89 3748 : sal_Int32 n = stream->readBytes(data, size);
90 3748 : s->Write(data.getConstArray(), n);
91 3748 : if (n < size) {
92 3456 : break;
93 : }
94 292 : }
95 3456 : s->Seek(0);
96 3456 : return s;
97 : }
98 :
99 3424 : static void loadImageFromStream(
100 : boost::shared_ptr< SvStream > pStream,
101 : OUString const & rPath, BitmapEx & rBitmap)
102 : {
103 3424 : if (rPath.endsWith(".png"))
104 : {
105 3420 : vcl::PNGReader aPNGReader( *pStream );
106 3420 : aPNGReader.SetIgnoreGammaChunk( true );
107 3420 : rBitmap = aPNGReader.Read();
108 : } else {
109 4 : ReadDIBBitmapEx(rBitmap, *pStream);
110 : }
111 3424 : }
112 :
113 : }
114 :
115 124 : ImplImageTree::ImplImageTree() { m_cacheIcons = true; }
116 :
117 124 : ImplImageTree::~ImplImageTree() {}
118 :
119 322 : bool ImplImageTree::checkStyle(OUString const & style)
120 : {
121 : bool exists;
122 :
123 : // using cache because setStyle is an expensive operation
124 : // setStyle calls resetPaths => closes any opened zip files with icons, cleans the icon cache, ...
125 322 : if (checkStyleCacheLookup(style, exists)) {
126 29 : return exists;
127 : }
128 :
129 293 : setStyle(style);
130 :
131 293 : exists = false;
132 293 : OUString aURL = m_path.first;
133 :
134 586 : osl::File aZip(aURL + ".zip");
135 293 : if (aZip.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) {
136 32 : aZip.close();
137 32 : exists = true;
138 : }
139 :
140 586 : osl::Directory aLookaside(aURL);
141 293 : if (aLookaside.open() == ::osl::FileBase::E_None) {
142 0 : aLookaside.close();
143 0 : exists = true;
144 0 : m_cacheIcons = false;
145 : } else {
146 293 : m_cacheIcons = true;
147 : }
148 293 : m_checkStyleCache[style] = exists;
149 586 : return exists;
150 : }
151 :
152 18090 : bool ImplImageTree::loadImage(
153 : OUString const & name, OUString const & style, BitmapEx & bitmap,
154 : bool localized, bool loadMissing )
155 : {
156 18090 : bool found = false;
157 : try {
158 18090 : found = doLoadImage(name, style, bitmap, localized);
159 0 : } catch (css::uno::RuntimeException &) {
160 0 : if (!loadMissing)
161 0 : throw;
162 : }
163 18090 : if (found || !loadMissing)
164 18090 : return found;
165 :
166 : SAL_INFO("vcl", "ImplImageTree::loadImage exception couldn't load \""
167 : << name << "\", fetching default image");
168 :
169 0 : return loadDefaultImage(style, bitmap);
170 : }
171 :
172 4161 : bool ImplImageTree::loadDefaultImage(
173 : OUString const & style,
174 : BitmapEx& bitmap)
175 : {
176 : return doLoadImage(
177 : OUString("res/grafikde.png"),
178 4161 : style, bitmap, false);
179 : }
180 :
181 :
182 22251 : bool ImplImageTree::doLoadImage(
183 : OUString const & name, OUString const & style, BitmapEx & bitmap,
184 : bool localized)
185 : {
186 22251 : setStyle(style);
187 22251 : if (m_cacheIcons && iconCacheLookup(name, localized, bitmap)) {
188 17121 : return true;
189 : }
190 5130 : if (!bitmap.IsEmpty()) {
191 0 : bitmap.SetEmpty();
192 : }
193 5130 : std::vector< OUString > paths;
194 5130 : paths.push_back(getRealImageName(name));
195 5130 : if (localized) {
196 4962 : sal_Int32 pos = name.lastIndexOf('/');
197 4962 : if (pos != -1) {
198 : // find() uses a reverse iterator, so push in reverse order.
199 4962 : std::vector< OUString > aFallbacks( Application::GetSettings().GetUILanguageTag().getFallbackStrings());
200 44658 : for (std::vector< OUString >::const_reverse_iterator it( aFallbacks.rbegin());
201 29772 : it != aFallbacks.rend(); ++it)
202 : {
203 9924 : paths.push_back( getRealImageName( createPath(name, pos, *it) ) );
204 4962 : }
205 : }
206 : }
207 5130 : bool found = false;
208 : try {
209 5130 : found = find(paths, bitmap);
210 0 : } catch (css::uno::RuntimeException &) {
211 0 : throw;
212 0 : } catch (const css::uno::Exception & e) {
213 : SAL_INFO("vcl", "ImplImageTree::doLoadImage exception " << e.Message);
214 : }
215 5130 : if (m_cacheIcons && found) {
216 3424 : m_iconCache[name.intern()] = std::make_pair(localized, bitmap);
217 : }
218 5130 : return found;
219 : }
220 :
221 94 : void ImplImageTree::shutDown() {
222 94 : m_style = OUString();
223 : // for safety; empty m_style means "not initialized"
224 94 : m_iconCache.clear();
225 94 : m_checkStyleCache.clear();
226 94 : m_iconLinkCache.clear();
227 94 : }
228 :
229 22544 : void ImplImageTree::setStyle(OUString const & style) {
230 : OSL_ASSERT(!style.isEmpty()); // empty m_style means "not initialized"
231 22544 : if (style != m_style) {
232 322 : m_style = style;
233 322 : resetPaths();
234 322 : m_iconCache.clear();
235 322 : m_iconLinkCache.clear();
236 322 : loadImageLinks();
237 : }
238 22544 : }
239 :
240 322 : void ImplImageTree::resetPaths() {
241 322 : OUString url( "$BRAND_BASE_DIR/share/config/" );
242 322 : rtl::Bootstrap::expandMacros(url);
243 322 : if ( m_style != "default" )
244 : {
245 293 : INetURLObject u(url);
246 : OSL_ASSERT(!u.HasError());
247 293 : bool ok = u.Append("images_" + m_style, INetURLObject::ENCODE_ALL);
248 : OSL_ASSERT(ok); (void) ok;
249 293 : url = u.GetMainURL(INetURLObject::NO_DECODE);
250 : }
251 : else
252 29 : url += "images";
253 644 : m_path = std::make_pair(
254 644 : url, css::uno::Reference< css::container::XNameAccess >());
255 322 : }
256 :
257 322 : bool ImplImageTree::checkStyleCacheLookup(
258 : OUString const & style, bool &exists)
259 : {
260 322 : CheckStyleCache::iterator i(m_checkStyleCache.find(style));
261 322 : if (i != m_checkStyleCache.end()) {
262 29 : exists = i->second;
263 29 : return true;
264 : } else {
265 293 : return false;
266 : }
267 : }
268 :
269 22251 : bool ImplImageTree::iconCacheLookup(
270 : OUString const & name, bool localized, BitmapEx & bitmap)
271 : {
272 22251 : IconCache::iterator i(m_iconCache.find(name));
273 22251 : if (i != m_iconCache.end() && i->second.first == localized) {
274 17121 : bitmap = i->second.second;
275 17121 : return true;
276 : } else {
277 5130 : return false;
278 : }
279 : }
280 :
281 5130 : bool ImplImageTree::find(
282 : std::vector< OUString > const & paths, BitmapEx & bitmap)
283 : {
284 5130 : if (!m_cacheIcons) {
285 0 : for (std::vector< OUString >::const_reverse_iterator j(
286 0 : paths.rbegin());
287 0 : j != paths.rend(); ++j)
288 : {
289 0 : osl::File file(m_path.first + "/" + *j);
290 0 : if (file.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) {
291 0 : loadImageFromStream( wrapFile(file), *j, bitmap );
292 0 : file.close();
293 0 : return true;
294 : }
295 0 : }
296 : }
297 :
298 5130 : if (!m_path.second.is()) {
299 : try {
300 1706 : m_path.second = css::packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), m_path.first + ".zip");
301 0 : } catch (const css::uno::RuntimeException &) {
302 0 : throw;
303 3412 : } catch (const css::uno::Exception & e) {
304 : SAL_INFO("vcl", "ImplImageTree::find exception "
305 : << e.Message << " for " << m_path.first);
306 1706 : return false;
307 : }
308 : }
309 30804 : for (std::vector< OUString >::const_reverse_iterator j(paths.rbegin());
310 20536 : j != paths.rend(); ++j)
311 : {
312 10268 : if (m_path.second->hasByName(*j)) {
313 3424 : css::uno::Reference< css::io::XInputStream > s;
314 3424 : bool ok = m_path.second->getByName(*j) >>= s;
315 : OSL_ASSERT(ok); (void) ok;
316 3424 : loadImageFromStream( wrapStream(s), *j, bitmap );
317 3424 : return true;
318 : }
319 : }
320 0 : return false;
321 : }
322 :
323 322 : void ImplImageTree::loadImageLinks()
324 : {
325 322 : const OUString aLinkFilename("links.txt");
326 :
327 322 : if (!m_cacheIcons)
328 : {
329 0 : osl::File file(m_path.first + "/" + aLinkFilename);
330 0 : if (file.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None)
331 : {
332 0 : parseLinkFile( wrapFile(file) );
333 0 : file.close();
334 0 : return;
335 0 : }
336 : }
337 :
338 322 : if ( !m_path.second.is() )
339 : {
340 : try
341 : {
342 322 : m_path.second = css::packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), m_path.first + ".zip");
343 0 : } catch (const css::uno::RuntimeException &) {
344 0 : throw;
345 : }
346 580 : catch (const css::uno::Exception & e)
347 : {
348 : SAL_INFO("vcl", "ImplImageTree::find exception "
349 : << e.Message << " for " << m_path.first);
350 290 : return;
351 : }
352 : }
353 32 : if ( m_path.second->hasByName(aLinkFilename) )
354 : {
355 32 : css::uno::Reference< css::io::XInputStream > s;
356 32 : bool ok = m_path.second->getByName(aLinkFilename) >>= s;
357 : OSL_ASSERT(ok); (void) ok;
358 :
359 32 : parseLinkFile( wrapStream(s) );
360 32 : return;
361 0 : }
362 : }
363 :
364 32 : void ImplImageTree::parseLinkFile(boost::shared_ptr< SvStream > pStream)
365 : {
366 32 : OString aLine;
367 64 : OUString aLink, aOriginal;
368 12640 : while ( pStream->ReadLine( aLine ) )
369 : {
370 12576 : sal_Int32 nIndex = 0;
371 12576 : if ( aLine.isEmpty() )
372 0 : continue;
373 12576 : aLink = OStringToOUString( aLine.getToken(0, ' ', nIndex), RTL_TEXTENCODING_UTF8 );
374 12576 : aOriginal = OStringToOUString( aLine.getToken(0, ' ', nIndex), RTL_TEXTENCODING_UTF8 );
375 12576 : if ( aLink.isEmpty() || aOriginal.isEmpty() )
376 : {
377 : SAL_INFO("vcl", "ImplImageTree::parseLinkFile: icon links.txt parse error. "
378 : "Link is incomplete." );
379 0 : continue;
380 : }
381 12576 : m_iconLinkCache[aLink] = aOriginal;
382 32 : }
383 32 : }
384 :
385 15054 : OUString const & ImplImageTree::getRealImageName(OUString const & name)
386 : {
387 15054 : IconLinkCache::iterator it(m_iconLinkCache.find(name));
388 15054 : if (it == m_iconLinkCache.end())
389 15054 : return name;
390 0 : return it->second;
391 465 : }
392 :
393 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|