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