xref: /AOO41X/main/vcl/source/gdi/impimagetree.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ***********************************************************************/
27 
28 #include "precompiled_vcl.hxx"
29 
30 #include "sal/config.h"
31 
32 #include <list>
33 #include <memory>
34 #include <utility>
35 #include <vector>
36 #include <hash_map>
37 
38 #include "com/sun/star/container/XNameAccess.hpp"
39 #include "com/sun/star/io/XInputStream.hpp"
40 #include "com/sun/star/lang/Locale.hpp"
41 #include "com/sun/star/uno/Any.hxx"
42 #include "com/sun/star/uno/Exception.hpp"
43 #include "com/sun/star/uno/Reference.hxx"
44 #include "com/sun/star/uno/RuntimeException.hpp"
45 #include "com/sun/star/uno/Sequence.hxx"
46 
47 #include "comphelper/processfactory.hxx"
48 
49 #include "osl/file.hxx"
50 #include "osl/diagnose.h"
51 
52 #include "rtl/bootstrap.hxx"
53 #include "rtl/string.h"
54 #include "rtl/textenc.h"
55 #include "rtl/ustrbuf.hxx"
56 #include "rtl/ustring.h"
57 #include "rtl/ustring.hxx"
58 
59 #include "sal/types.h"
60 
61 #include "tools/stream.hxx"
62 #include "tools/urlobj.hxx"
63 
64 #include "vcl/bitmapex.hxx"
65 #include "vcl/pngread.hxx"
66 #include "vcl/settings.hxx"
67 #include "vcl/svapp.hxx"
68 
69 #include "impimagetree.hxx"
70 
71 namespace {
72 
73 namespace css = com::sun::star;
74 
75 rtl::OUString createPath(
76     rtl::OUString const & name, sal_Int32 pos, rtl::OUString const & locale)
77 {
78     rtl::OUStringBuffer b(name.copy(0, pos + 1));
79     b.append(locale);
80     b.append(name.copy(pos));
81     return b.makeStringAndClear();
82 }
83 
84 std::auto_ptr< SvStream > wrapStream(
85     css::uno::Reference< css::io::XInputStream > const & stream)
86 {
87     // This could use SvInputStream instead if that did not have a broken
88     // SeekPos implementation for an XInputStream that is not also XSeekable
89     // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
90     // l. 593):
91     OSL_ASSERT(stream.is());
92     std::auto_ptr< SvStream > s(new SvMemoryStream);
93     for (;;) {
94         css::uno::Sequence< sal_Int8 > data;
95         sal_Int32 const size = 30000;
96         sal_Int32 n = stream->readBytes(data, size);
97         s->Write(data.getConstArray(), n);
98         if (n < size) {
99             break;
100         }
101     }
102     s->Seek(0);
103     return s;
104 }
105 
106 void loadFromStream(
107     css::uno::Reference< css::io::XInputStream > const & stream,
108     rtl::OUString const & path, BitmapEx & bitmap)
109 {
110     std::auto_ptr< SvStream > s(wrapStream(stream));
111     if (path.endsWithAsciiL(RTL_CONSTASCII_STRINGPARAM(".png")))
112 	{
113 		vcl::PNGReader aPNGReader( *s );
114 		aPNGReader.SetIgnoreGammaChunk( sal_True );
115         bitmap = aPNGReader.Read();
116     } else {
117         *s >> bitmap;
118     }
119 }
120 
121 }
122 
123 ImplImageTree::ImplImageTree() {}
124 
125 ImplImageTree::~ImplImageTree() {}
126 
127 bool ImplImageTree::checkStyle(rtl::OUString const & style)
128 {
129     bool exists;
130 
131     // using cache because setStyle is an expensive operation
132     // setStyle calls resetZips => closes any opened zip files with icons, cleans the icon cache, ...
133     if (checkStyleCacheLookup(style, exists)) {
134         return exists;
135     }
136 
137     setStyle(style);
138 
139     exists = false;
140     const rtl::OUString sBrandURLSuffix(RTL_CONSTASCII_USTRINGPARAM("_brand.zip"));
141     for (Zips::iterator i(m_zips.begin()); i != m_zips.end() && !exists;) {
142         ::rtl::OUString aZipURL = i->first;
143         sal_Int32 nFromIndex = aZipURL.getLength() - sBrandURLSuffix.getLength();
144         // skip brand-specific icon themes; they are incomplete and thus not useful for this check
145         if (nFromIndex < 0 || !aZipURL.match(sBrandURLSuffix, nFromIndex)) {
146             osl::File aZip(aZipURL);
147             if (aZip.open(OpenFlag_Read) == ::osl::FileBase::E_None) {
148                 aZip.close();
149                 exists = true;
150             }
151         }
152         ++i;
153     }
154     m_checkStyleCache[style] = exists;
155     return exists;
156 }
157 
158 bool ImplImageTree::loadImage(
159     rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap,
160     bool localized)
161 {
162     setStyle(style);
163     if (iconCacheLookup(name, localized, bitmap)) {
164         return true;
165     }
166     if (!bitmap.IsEmpty()) {
167         bitmap.SetEmpty();
168     }
169     std::vector< rtl::OUString > paths;
170     paths.push_back(name);
171     if (localized) {
172         sal_Int32 pos = name.lastIndexOf('/');
173         if (pos != -1) {
174             css::lang::Locale const & loc =
175                 Application::GetSettings().GetUILocale();
176             paths.push_back(createPath(name, pos, loc.Language));
177             if (loc.Country.getLength() != 0) {
178                 rtl::OUStringBuffer b(loc.Language);
179                 b.append(sal_Unicode('-'));
180                 b.append(loc.Country);
181                 rtl::OUString p(createPath(name, pos, b.makeStringAndClear()));
182                 paths.push_back(p);
183                 if (loc.Variant.getLength() != 0) {
184                     b.append(p);
185                     b.append(sal_Unicode('-'));
186                     b.append(loc.Variant);
187                     paths.push_back(
188                         createPath(name, pos, b.makeStringAndClear()));
189                 }
190             }
191         }
192     }
193     bool found = false;
194     try {
195         found = find(paths, bitmap);
196     } catch (css::uno::RuntimeException &) {
197         throw;
198     } catch (css::uno::Exception & e) {
199         OSL_TRACE(
200             "ImplImageTree::loadImage exception \"%s\"",
201             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
202     }
203     if (found) {
204         m_iconCache[name.intern()] = std::make_pair(localized, bitmap);
205     }
206     return found;
207 }
208 
209 void ImplImageTree::shutDown() {
210     m_style = rtl::OUString();
211         // for safety; empty m_style means "not initialized"
212     m_zips.clear();
213     m_iconCache.clear();
214     m_checkStyleCache.clear();
215 }
216 
217 void ImplImageTree::setStyle(rtl::OUString const & style) {
218     OSL_ASSERT(style.getLength() != 0); // empty m_style means "not initialized"
219     if (style != m_style) {
220         m_style = style;
221         resetZips();
222         m_iconCache.clear();
223     }
224 }
225 
226 void ImplImageTree::resetZips() {
227     m_zips.clear();
228     {
229         rtl::OUString url(
230             RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR/program/edition/images.zip"));
231         rtl::Bootstrap::expandMacros(url);
232         INetURLObject u(url);
233         OSL_ASSERT(!u.HasError());
234         m_zips.push_back(
235             std::make_pair(
236                 u.GetMainURL(INetURLObject::NO_DECODE),
237                 css::uno::Reference< css::container::XNameAccess >()));
238     }
239     {
240         rtl::OUString url(
241             RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR/share/config"));
242         rtl::Bootstrap::expandMacros(url);
243         INetURLObject u(url);
244         OSL_ASSERT(!u.HasError());
245         rtl::OUStringBuffer b;
246         b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
247         b.append(m_style);
248         b.appendAscii(RTL_CONSTASCII_STRINGPARAM("_brand.zip"));
249         bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL);
250         OSL_ASSERT(ok); (void) ok;
251         m_zips.push_back(
252             std::make_pair(
253                 u.GetMainURL(INetURLObject::NO_DECODE),
254                 css::uno::Reference< css::container::XNameAccess >()));
255     }
256     {
257         rtl::OUString url(
258             RTL_CONSTASCII_USTRINGPARAM(
259                 "$BRAND_BASE_DIR/share/config/images_brand.zip"));
260         rtl::Bootstrap::expandMacros(url);
261         m_zips.push_back(
262             std::make_pair(
263                 url, css::uno::Reference< css::container::XNameAccess >()));
264     }
265     {
266         rtl::OUString url(
267             RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/share/config"));
268         rtl::Bootstrap::expandMacros(url);
269         INetURLObject u(url);
270         OSL_ASSERT(!u.HasError());
271         rtl::OUStringBuffer b;
272         b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_"));
273         b.append(m_style);
274         b.appendAscii(RTL_CONSTASCII_STRINGPARAM(".zip"));
275         bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL);
276         OSL_ASSERT(ok); (void) ok;
277         m_zips.push_back(
278             std::make_pair(
279                 u.GetMainURL(INetURLObject::NO_DECODE),
280                 css::uno::Reference< css::container::XNameAccess >()));
281     }
282     if ( m_style.equals(::rtl::OUString::createFromAscii("default")) )
283     {
284         rtl::OUString url(
285             RTL_CONSTASCII_USTRINGPARAM(
286                 "$OOO_BASE_DIR/share/config/images.zip"));
287         rtl::Bootstrap::expandMacros(url);
288         m_zips.push_back(
289             std::make_pair(
290                 url, css::uno::Reference< css::container::XNameAccess >()));
291     }
292 }
293 
294 bool ImplImageTree::checkStyleCacheLookup(
295     rtl::OUString const & style, bool &exists)
296 {
297     CheckStyleCache::iterator i(m_checkStyleCache.find(style));
298     if (i != m_checkStyleCache.end()) {
299         exists = i->second;
300         return true;
301     } else {
302         return false;
303     }
304 }
305 
306 bool ImplImageTree::iconCacheLookup(
307     rtl::OUString const & name, bool localized, BitmapEx & bitmap)
308 {
309     IconCache::iterator i(m_iconCache.find(name));
310     if (i != m_iconCache.end() && i->second.first == localized) {
311         bitmap = i->second.second;
312         return true;
313     } else {
314         return false;
315     }
316 }
317 
318 bool ImplImageTree::find(
319     std::vector< rtl::OUString > const & paths, BitmapEx & bitmap)
320 {
321     for (Zips::iterator i(m_zips.begin()); i != m_zips.end();) {
322         if (!i->second.is()) {
323             css::uno::Sequence< css::uno::Any > args(1);
324             args[0] <<= i->first;
325             try {
326                 i->second.set(
327                     comphelper::createProcessComponentWithArguments(
328                         rtl::OUString(
329                             RTL_CONSTASCII_USTRINGPARAM(
330                                 "com.sun.star.packages.zip.ZipFileAccess")),
331                         args),
332                     css::uno::UNO_QUERY_THROW);
333             } catch (css::uno::RuntimeException &) {
334                 throw;
335             } catch (css::uno::Exception & e) {
336                 OSL_TRACE(
337                     "ImplImageTree::find exception \"%s\"",
338                     rtl::OUStringToOString(
339                         e.Message, RTL_TEXTENCODING_UTF8).getStr());
340                 i = m_zips.erase(i);
341                 continue;
342             }
343         }
344         for (std::vector< rtl::OUString >::const_reverse_iterator j(
345                  paths.rbegin());
346              j != paths.rend(); ++j)
347         {
348             if (i->second->hasByName(*j)) {
349                 css::uno::Reference< css::io::XInputStream > s;
350                 bool ok = i->second->getByName(*j) >>= s;
351                 OSL_ASSERT(ok); (void) ok;
352                 loadFromStream(s, *j, bitmap);
353                 return true;
354             }
355         }
356         ++i;
357     }
358     return false;
359 }
360