package com.limegroup.gnutella.gui.themes; import java.io.File; import java.io.IOException; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.Enumeration; import java.util.Locale; import java.util.StringTokenizer; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import com.limegroup.gnutella.ErrorService; import com.limegroup.gnutella.MessageService; import com.limegroup.gnutella.settings.FileSetting; import com.limegroup.gnutella.settings.LimeProps; import com.limegroup.gnutella.util.CommonUtils; import com.limegroup.gnutella.util.Expand; import com.limegroup.gnutella.util.FileUtils; /** * Class for handling all LimeWire settings that are stored to disk. To * add a new setting, simply add a new public static member to the list * of settings. Construct settings using the <tt>FACTORY</tt> instance * from the <tt>AbstractSettings</tt> superclass. Each setting factory * constructor takes the name of the key and the default value, and all * settings are typed. Choose the correct <tt>Setting</tt> factory constructor * for your setting type. It is also important to choose a unique string key * for your setting name -- otherwise there will be conflicts, and a runtime * exception will be thrown. */ public final class ThemeSettings extends LimeProps { private ThemeSettings() {} /** * The extension for theme packs to allow people to search for them -- * stands for "LimeWire Theme Pack". */ public static final String EXTENSION = "lwtp"; public static final File THEME_DIR_FILE = new File(CommonUtils.getUserSettingsDir(), "themes"); /** * The normal 'LimeWire' theme. */ public static final String LIMEWIRE_THEME_NAME = "limewire_theme."+EXTENSION; /** * The default name of the theme file name for OS X. */ public static final String PINSTRIPES_OSX_THEME_NAME = "pinstripes_theme_osx."+EXTENSION; /** * The metal theme name. */ public static final String BRUSHED_METAL_OSX_THEME_NAME = "brushed_metal_theme_osx."+EXTENSION; /** * The default name of the windows laf theme file name. */ public static final String WINDOWS_LAF_THEME_NAME = "windows_theme."+EXTENSION; /** * The default name of the gtk laf theme file name. */ public static final String GTK_LAF_THEME_NAME = "GTK_theme." + EXTENSION; /** * The default name of the theme file name for non-OS X pro users. */ public static final String PRO_THEME_NAME = "limewirePro_theme."+EXTENSION; /** * The name for the unknown theme file. */ public static final String OTHER_THEME_NAME = "other_theme." + EXTENSION; /** * The full path to the LimeWire theme file. */ public static final File LIMEWIRE_THEME_FILE = new File(THEME_DIR_FILE, LIMEWIRE_THEME_NAME); /** * The full path to the default theme file on OS X. */ static final File PINSTRIPES_OSX_THEME_FILE = new File(THEME_DIR_FILE, PINSTRIPES_OSX_THEME_NAME); /** * The full path to the metal theme file on OS X. */ static final File BRUSHED_METAL_OSX_THEME_FILE = new File(THEME_DIR_FILE, BRUSHED_METAL_OSX_THEME_NAME); /** * The full path to the windows theme file for the windows LAF */ static final File WINDOWS_LAF_THEME_FILE = new File(THEME_DIR_FILE, WINDOWS_LAF_THEME_NAME); /** * The full path to the GTK theme file for the GTK LAF */ static final File GTK_LAF_THEME_FILE = new File(THEME_DIR_FILE, GTK_LAF_THEME_NAME); /** * The full path to the pro only theme. */ static final File PRO_THEME_FILE = new File(THEME_DIR_FILE, PRO_THEME_NAME); /** * The path for the 'other' theme name. */ static final File OTHER_THEME_FILE = new File(THEME_DIR_FILE, OTHER_THEME_NAME); /** * Statically expand any zip files in our jar if they're newer than the * ones on disk. */ static { File themesJar = new File("themes.jar"); // workaround for when the jar file is only in your classpath -- // this uses the default theme instead of the jar to check // the timestamp -- added by Jens-Uwe Mager // this also is used for running LimeWire from CVS if(!themesJar.isFile()) { String url = ThemeSettings.class.getClassLoader(). getResource(LIMEWIRE_THEME_NAME).toString(); if (url != null && url.startsWith("jar:file:")) { url = url.substring("jar:file:".length(), url.length()); url = url.substring(0, url.length()- LIMEWIRE_THEME_NAME.length()-"!/".length()); themesJar = new File(url); } } if(themesJar.isFile()) { ZipFile zf = null; try { long jarMod = themesJar.lastModified(); zf = new ZipFile(themesJar); Enumeration entries = zf.entries(); while(entries.hasMoreElements()) { ZipEntry ze = (ZipEntry)entries.nextElement(); String name = ze.getName(); if(!name.endsWith(".lwtp")) continue; File existingFile = new File(THEME_DIR_FILE, name); if(existingFile.isFile()) { if(jarMod > existingFile.lastModified()) copyZipWithNewTimestamp(name, existingFile); } else if(!existingFile.exists()) { copyZipWithNewTimestamp(name, existingFile); } } } catch(IOException ioe) { ErrorService.error(ioe); } finally { if(zf != null) { try { zf.close(); } catch(IOException ignored) {} } } } } /** * Utility method that copies and expends the specified theme * file from the themes jar. * * @param name the name of the file in the jar * @param themeFile the location on disk to copy the zip to */ private static void copyZipWithNewTimestamp(String name, File themeFile) { CommonUtils.copyResourceFile(name, themeFile, true); File themeDir = extractThemeDir(themeFile); expandTheme(themeFile, themeDir, true); } /** * Expands the specified theme zip file to the specified directory. * * @param themeFile the theme zip file to expand * @param themeDir the directory to expand to -- the themes directory * plus the name of the theme * @param overwrite whether or not to force the overwriting of existing * files in the destination folder when we expand the zip, regardless * of File/ZipEntry timestamp */ static boolean expandTheme(File themeFile, File themeDir, boolean overwrite) { themeDir.mkdirs(); try { FileUtils.setWriteable(themeDir); Expand.expandFile(themeFile, themeDir, overwrite); } catch(ZipException ze) { // invalid theme, tell the user. MessageService.showError("ERROR_APPLYING_INVALID_THEME_FILE"); return false; } catch(IOException e) { // this should never really happen, so report it ErrorService.error(e); return false; } return true; } /** * Convenience method for determing in the path of the themes directory * for a given theme file. The directory is the path of the themes * directory plus the name of the theme. * * @param themeFile the <tt>File</tt> instance denoting the location * of the theme file on disk * @return a new <tt>File</tt> instance denoting the appropriate path * for the directory for this specific theme */ static File extractThemeDir(File themeFile) { String dirName = themeFile.getName(); dirName = dirName.substring(0, dirName.length()-5); return new File(new File(CommonUtils.getUserSettingsDir(),"themes"), dirName); } /** * Determines whether or not the specified file is a theme file. */ static boolean isThemeFile(File f) { return f.getName().toLowerCase().endsWith("." + EXTENSION); } /** * Determines whether or not the current theme file is the default theme * file. * * @return <tt>true</tt> if the current theme file is the default, * otherwise <tt>false</tt> */ public static boolean isDefaultTheme() { return THEME_FILE.getValue().equals(THEME_DEFAULT); } /** * Determines if the current theme is the GTK theme. */ public static boolean isGTKTheme() { return THEME_FILE.getValue().equals(GTK_LAF_THEME_FILE); } /** * Determines whether or not the current theme is the windows theme, * designed to be used for the windows laf. * @return <tt>true</tt> if the current theme is the windows theme, * otherwise <tt>false</tt> */ public static boolean isWindowsTheme() { return THEME_FILE.getValue().equals(WINDOWS_LAF_THEME_FILE); } /** * Determines if the theme is the brushed metal theme. */ public static boolean isBrushedMetalTheme() { return THEME_FILE.getValue().equals(BRUSHED_METAL_OSX_THEME_FILE); } /** * Determines if the theme is the pinstripes theme. */ public static boolean isPinstripesTheme() { return THEME_FILE.getValue().equals(PINSTRIPES_OSX_THEME_FILE); } /** * Determines if the current theme is the native OSX theme. */ public static boolean isNativeOSXTheme() { return CommonUtils.isMacOSX() && (isPinstripesTheme() || isBrushedMetalTheme()); } /** * Determines if the current theme is the native theme. */ public static boolean isNativeTheme() { return isNativeOSXTheme() || isWindowsTheme() || isGTKTheme(); } /** * Determines if the current theme is the 'other' theme. */ public static boolean isOtherTheme() { return THEME_FILE.getValue().equals(OTHER_THEME_FILE); } /** * Determines whether or not the current theme is valid. */ public static boolean isValid() { if(isOtherTheme()) { String name = getOtherLF(); if(name != null) { try { Class.forName(name); return true; } catch(ClassNotFoundException nfe) {} } return false; } return true; } /** * Gets the L&F that should be used if this is the 'other' theme. */ public static String getOtherLF() { BufferedReader in = null; try { in = new BufferedReader( new InputStreamReader( new FileInputStream( new File(THEME_DIR_FILE, "other_theme/name.txt")))); String classname = in.readLine(); if(classname != null) return classname.trim(); } catch(IOException ignored) { } finally { if(in != null) try { in.close(); } catch(IOException ignored) {} } return null; } /** * Sets the other L&F classname. */ public static void setOtherLF(String classname) { BufferedWriter out = null; try { out = new BufferedWriter( new FileWriter( new File(THEME_DIR_FILE, "other_theme/name.txt"), false)); out.write(classname); out.flush(); } catch(IOException ignored) { } finally { if(out != null) try { out.close(); } catch(IOException ignored) {} } } /** * Formats a theme name, removing the underscore characters, * capitalizing the first letter of each word, and removing * the 'lwtp'. */ public static String formatName(String name) { // strip off the .lwtp name = name.substring(0, name.length()-5); StringBuffer formatted = new StringBuffer(name.length()); StringTokenizer st = new StringTokenizer(name, "_"); String next; for(; st.hasMoreTokens(); ) { next = st.nextToken(); String lower = next.toLowerCase(); if(lower.equals("osx")) next = "(OSX)"; else if(lower.equals("limewire")) next = "LimeWire"; else if(lower.equals("limewirepro")) next = "LimeWire PRO"; formatted.append(" " + next.substring(0,1).toUpperCase(Locale.US)); if(next.length() > 1) formatted.append(next.substring(1)); } return formatted.toString().trim(); } /** * Setting for the default theme file to use for LimeWire display. */ public static final File THEME_DEFAULT; public static final File THEME_DEFAULT_DIR; static { File theme, dir; if(CommonUtils.isMacOSX()) { theme = PINSTRIPES_OSX_THEME_FILE; dir = new File(THEME_DIR_FILE, "pinstripes_theme_osx"); } else if(CommonUtils.isPro()) { theme = PRO_THEME_FILE; dir = new File(THEME_DIR_FILE, "limewirePro_theme"); } else if(CommonUtils.isWindowsXP()) { theme = WINDOWS_LAF_THEME_FILE; dir = new File(THEME_DIR_FILE, "windows_theme"); } else if(CommonUtils.isLinux() && CommonUtils.isJava15OrLater()) { theme = GTK_LAF_THEME_FILE; dir = new File(THEME_DIR_FILE, "GTK_theme"); } else { theme = LIMEWIRE_THEME_FILE; dir = new File(THEME_DIR_FILE, "limewire_theme"); } THEME_DEFAULT = theme; THEME_DEFAULT_DIR = dir; } /** * Setting for the file name of the theme file. */ public static final FileSetting THEME_FILE = FACTORY.createFileSetting("THEME_FILE", THEME_DEFAULT); /** * Setting for the file name of the theme directory. */ public static final FileSetting THEME_DIR = FACTORY.createFileSetting("THEME_DIR", THEME_DEFAULT_DIR); }