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