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