/* This file is part of leafdigital leafChat. leafChat is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. leafChat is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with leafChat. If not, see <http://www.gnu.org/licenses/>. Copyright 2011 Samuel Marshall. */ package com.leafdigital.ui; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.util.*; import java.util.List; import java.util.zip.*; import org.w3c.dom.Document; import textlayout.LayoutException; import textlayout.stylesheet.Stylesheet; import util.*; import util.xml.*; import com.leafdigital.ui.api.Theme; import leafchat.core.api.BugException; /** * Implementation of {@link Theme} interface. */ public class ThemeImp implements Theme, Comparable<ThemeImp> { /** Parent theme, if any */ private ThemeImp parent; /** File that was used to create this theme */ private File f; /** XML document */ private Document d; /** Stylesheet if included */ private Stylesheet ss=null; private Map<String, BufferedImage> imageCache = new HashMap<String, BufferedImage>(); void setParent(ThemeImp parent) { this.parent=parent; } @Override public int compareTo(ThemeImp other) { if(equals(other)) return 0; int names= getStringProperty(Theme.META,Theme.META_NAME,"?").compareToIgnoreCase( other.getStringProperty(Theme.META,Theme.META_NAME,"?")); if(names!=0) return names; return f.compareTo(other.f); } @Override public boolean equals(Object o) { return f.equals(((ThemeImp)o).f); } @Override public int hashCode() { return f.hashCode(); } ThemeImp(File f) throws IOException { this.f=f; getData("theme.xml",new ZipDataHandler() { @Override public void handle(InputStream is) throws IOException { try { d=XML.parse(is); } catch(XMLException e) { IOException ioe=new IOException(e.getMessage()); ioe.initCause(e); throw ioe; } } },false,false); getData("theme.css",new ZipDataHandler() { @Override public void handle(InputStream is) throws IOException { try { ss=new Stylesheet(is); } catch(LayoutException le) { IOException ioe=new IOException(le.getMessage()); ioe.initCause(le); throw ioe; } } },true,false); } private interface ZipDataHandler { public void handle(InputStream is) throws IOException; } private void getData(String sFile,ZipDataHandler zdh,boolean allowEmpty,boolean useParents) throws IOException { ZipFile zf=new ZipFile(f); try { ZipEntry ze=zf.getEntry(sFile); if(ze==null) { if(useParents && parent!=null) { parent.getData(sFile,zdh,allowEmpty,useParents); return; } if(allowEmpty) return; else throw new IOException("No such entry in theme zip: "+sFile); } zdh.handle(zf.getInputStream(ze)); } finally { zf.close(); } } File getFile() { return f; } @Override public BufferedImage getImageProperty(String themeType, String property, boolean transparency, Class<?> defaultReference, String defaultFilename) { String key=(themeType==null ? "direct\n" : themeType)+"\n"+property; BufferedImage bi=imageCache.get(key); if(bi!=null) return bi; String filename; if(themeType==null) { filename=property; } else { filename=getStringProperty(themeType,property,null); } if(filename==null) { if(defaultReference!=null && defaultFilename!=null) { try { bi=ensureBuffered( GraphicsUtils.loadImage(defaultReference.getResource(defaultFilename)), transparency); } catch(IOException e) { throw new BugException(e); } imageCache.put(key,bi); return bi; } return null; } final String val=filename; final List<Image> result = new LinkedList<Image>(); try { getData(val,new ZipDataHandler() { @Override public void handle(InputStream is) throws IOException { result.add(GraphicsUtils.loadImage(is,val)); } },true,true); if(result.isEmpty()) return null; Image i=result.get(0); bi=ensureBuffered(i,transparency); imageCache.put(key,bi); return bi; } catch(IOException ioe) { if(parent!=null && themeType!=null) return parent.getImageProperty(themeType,property,transparency,null,null); else return null; } } private BufferedImage ensureBuffered(Image i,boolean transparency) { BufferedImage bi=null; if(i instanceof BufferedImage) bi=(BufferedImage)i; if(bi==null || (!transparency && bi.getType()!=BufferedImage.TYPE_INT_RGB)) { bi=new BufferedImage(i.getWidth(null),i.getHeight(null),BufferedImage.TYPE_INT_ARGB); bi.createGraphics().drawImage(i,0,0,null); } return bi; } @Override public int getIntProperty(String themeType,String property,int def) { String val=getStringProperty(themeType,property,null); if(val==null) return def; try { return Integer.parseInt(val); } catch(NumberFormatException nfe) { return def; } } @Override public boolean getBooleanProperty(String themeType,String property,boolean def) { String val=getStringProperty(themeType,property,null); return "y".equals(val) ? true : "n".equals(val) ? false : def; } @Override public String getStringProperty(String themeType,String property,String def) { try { return XML.getChildText(XML.getChild(d.getDocumentElement(),themeType),property); } catch(XMLException e) { if(parent!=null) return parent.getStringProperty(themeType,property,def); else return def; } } @Override public Stylesheet[] getStylesheets() { LinkedList<Stylesheet> found = new LinkedList<Stylesheet>(); ThemeImp current=this; while(current!=null) { if(current.ss!=null) found.addFirst(current.ss); current=current.parent; } return found.toArray(new Stylesheet[found.size()]); } @Override public File getLocation() { return f; } }