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