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 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 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 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 111 ImplImageTree::ImplImageTree() {} 112 113 ImplImageTree::~ImplImageTree() {} 114 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 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 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 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 214 void ImplImageTree::resetZips() { 215 m_zips.clear(); 216 { 217 rtl::OUString url( 218 RTL_CONSTASCII_USTRINGPARAM("$BRAND_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("$BRAND_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 "$BRAND_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 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 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 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