// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program 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 2 of the License, or (at your option) any later version.
//
// This program 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 this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: ThemeSupport.java,v 1.12 2007/03/19 15:49:45 spyromus Exp $
//
package com.salas.bb.views.themes;
import com.jgoodies.uif.util.ResourceUtils;
import com.salas.bb.utils.i18n.Strings;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* Loader of themes available for the current OS.
*/
public final class ThemeSupport
{
private static final Logger LOG = Logger.getLogger(ThemeSupport.class.getName());
private static final char ZIP_SEPARATOR = '/';
private static final String THEMES_INDEX = "resources/themes/index.txt";
private static List<ITheme> themes;
private static ITheme defaultTheme;
private static String currentOS;
private static final String KEY_NAME = "name";
private static final String KEY_TITLE = "title";
private static final String KEY_HIDDEN = "hidden";
/**
* Hidden constructor of utility class.
*/
private ThemeSupport()
{
}
/**
* Returns list of available themes.
*
* @return themes list.
*/
public static synchronized List<ITheme> getThemes()
{
if (themes == null) themes = loadThemes();
return themes;
}
/**
* Returns default theme.
*
* @return default theme.
*/
public static synchronized ITheme getDefaultTheme()
{
if (defaultTheme == null) defaultTheme = loadDefaultTheme();
return defaultTheme;
}
/**
* Looks up theme by its name.
*
* @param name name.
*
* @return theme or NULL if it wasn't found.
*/
public static ITheme getThemeByName(String name)
{
for (ITheme theme : getThemes()) if (theme.getName().equals(name)) return theme;
return null;
}
/**
* Loads list of themes available for this platform.
*
* @return list of themes.
*/
private static List<ITheme> loadThemes()
{
ITheme aDefaultTheme = getDefaultTheme();
return loadThemes(aDefaultTheme);
}
/**
* Loads list of themes available for this platform.
*
* @param aDefaultTheme default theme to use.
*
* @return list of themes.
*/
private static List<ITheme> loadThemes(ITheme aDefaultTheme)
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("Calling loadThemes (" + aDefaultTheme + ")");
}
URL[] themesUrls = getThemesUrls();
List<ITheme> someThemes = new ArrayList<ITheme>(themesUrls.length);
for (URL themeUrl : themesUrls)
{
ITheme theme = loadTheme(themeUrl, aDefaultTheme);
if (theme != null) someThemes.add(theme);
}
return someThemes;
}
/**
* Loads default theme for this platform.
*
* @return default theme.
*/
private static ITheme loadDefaultTheme()
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("Calling LoadDefaultTheme()");
}
return loadTheme(getThemeUrl("default.theme"), new LAFTheme());
}
/**
* Loads theme from definition and assigns specified default theme.
*
* @param themeUrl URL of the theme definition.
* @param aDefaultTheme default theme to assign.
*
* @return theme or NULL if not defined for this OS.
*
* @throws IllegalArgumentException if defaultTheme is NULL or URL is NULL or invalid.
*/
private static ITheme loadTheme(URL themeUrl, ITheme aDefaultTheme)
{
if (themeUrl == null)
throw new IllegalArgumentException(Strings.error("unspecified.theme.url"));
String os = getCurrentOS();
ITheme theme;
try
{
Properties properties = new Properties();
properties.load(themeUrl.openStream());
theme = createTheme(properties, aDefaultTheme, os);
} catch (IOException e)
{
throw new IllegalArgumentException(Strings.error("invalid.theme.url"));
}
return theme;
}
/**
* Creates theme using properties if it isn't set that this theme should be hidden for the
* given OS.
*
* @param aProperties properties to use for theme.
* @param aDefaultTheme default theme.
* @param aOs OS to create theme for.
*
* @return theme or NULL if theme cannot be created for the OS.
*/
static ITheme createTheme(Properties aProperties, ITheme aDefaultTheme, String aOs)
{
if (aDefaultTheme == null)
throw new IllegalArgumentException(Strings.error("unspecified.default.theme"));
if (aProperties == null)
throw new IllegalArgumentException(Strings.error("unspecified.properties"));
if (aOs == null)
throw new IllegalArgumentException(Strings.error("unspecified.os"));
String name = aProperties.getProperty(KEY_NAME);
String title = aProperties.getProperty(KEY_TITLE);
String hidden = aProperties.getProperty(KEY_HIDDEN);
ITheme theme = null;
boolean isHidden = name == null || title == null || isInList(hidden, aOs);
if (!isHidden)
{
aProperties = preProcessProperties(aProperties, aOs);
theme = new Theme(name, title, aProperties, aDefaultTheme);
}
return theme;
}
/**
* Takes values, specific to the OS, and puts them over the simple key versions.
*
* @param aProperties properties.
* @param aOs OS.
*
* @return processed properties version.
*/
static Properties preProcessProperties(Properties aProperties, String aOs)
{
final Set keySet = aProperties.keySet();
final String[] keys = (String[])keySet.toArray(new String[keySet.size()]);
aOs = "." + aOs;
int suffixLength = aOs.length();
for (String key : keys)
{
if (key.endsWith(aOs))
{
String baseKey = key.substring(0, key.length() - suffixLength);
aProperties.setProperty(baseKey, aProperties.getProperty(key));
}
}
return aProperties;
}
/**
* Accepts comma-delimetered list and string and tells if the string in this list.
*
* @param list comma-delimetered list.
* @param str string to look for.
*
* @return TRUE if string is found in list.
*/
private static boolean isInList(String list, String str)
{
if (list == null) return false;
str = str.toLowerCase();
StringTokenizer tokenizer = new StringTokenizer(list, ",");
boolean found = false;
while (!found && tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken().trim().toLowerCase();
found = token.equals(str);
}
return found;
}
/**
* Returns the URL to the theme file definition.
*
* @param themeFileName file name.
*
* @return URL.
*/
private static URL getThemeUrl(String themeFileName)
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("Calling getThemeUrl(" + themeFileName + ")");
}
return ResourceUtils.getURL("resources/themes/" + themeFileName);
}
/**
* Returns current OS.
*
* @return 'windows', 'linux' or 'mac'.
*/
private static synchronized String getCurrentOS()
{
if (currentOS == null) currentOS = detectCurrentOS();
return currentOS;
}
/**
* Detects current OS.
*
* @return current OS.
*/
private static String detectCurrentOS()
{
String os = "mac";
String propOsName = System.getProperty("os.name");
if (Pattern.compile("linux", Pattern.CASE_INSENSITIVE).matcher(propOsName).find())
{
os = "linux";
} else if (Pattern.compile("windows", Pattern.CASE_INSENSITIVE).matcher(propOsName).find())
{
os = "windows";
}
return os;
}
/**
* Finds all available themes and returns their URL's.
*
* @return URL's of found themes.
*/
private static URL[] getThemesUrls()
{
List<URL> themesUrlsList = new ArrayList<URL>();
String directoryName = getDirectoryName(THEMES_INDEX);
InputStream in = null;
try
{
in = ResourceUtils.getInputStream(THEMES_INDEX);
if (null == in) return new URL[0];
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
try
{
String line;
do
{
line = reader.readLine();
if (line != null)
{
line = line.trim();
if (line.length() > 0)
themesUrlsList.add(ResourceUtils.getURL(directoryName + line));
}
} while (line != null);
reader.close();
} catch (IOException e)
{
LOG.log(Level.WARNING, MessageFormat.format(
Strings.error("failed.to.read.tips.index"),
THEMES_INDEX), e);
}
} finally
{
try
{
if (in != null) in.close();
} catch (IOException e1)
{
// That's OK.
}
}
return themesUrlsList.toArray(new URL[themesUrlsList.size()]);
}
/**
* Answers the directory name for the given file name.
*
* @param filename file name.
*
* @return the directory name.
*/
private static String getDirectoryName(String filename)
{
int lastSeparatorIndex = filename.lastIndexOf(ZIP_SEPARATOR);
return (lastSeparatorIndex == -1) ? "" : filename.substring(0, lastSeparatorIndex + 1);
}
/**
* Loads and adds a theme by its URL.
*
* @param themeURL theme URL.
*/
public static void addTheme(URL themeURL)
{
getThemes().add(loadTheme(themeURL, getDefaultTheme()));
}
public static class FontsComboBoxModel extends DefaultComboBoxModel
{
private static final java.util.List fontFamilies;
static
{
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
fontFamilies = Arrays.asList(ge.getAvailableFontFamilyNames());
}
/**
* Returns size of the list.
*
* @return size.
*/
public int getSize()
{
return fontFamilies.size();
}
/**
* Returns element at specified index.
*
* @param index index.
*
* @return element.
*/
public Object getElementAt(int index)
{
return fontFamilies.get(index);
}
/**
* Returns the index-position of the specified object in the list.
*
* @param anObject object.
*
* @return an int representing the index position, where 0 is the first position
*/
public int getIndexOf(Object anObject)
{
return fontFamilies.indexOf(anObject);
}
}
/**
* Immutable list of themes for any combo-box.
*/
public static class ThemesComboBoxModel extends DefaultComboBoxModel
{
/**
* Returns size of the list.
*
* @return size.
*/
public int getSize()
{
return getThemes().size();
}
/**
* Returns element at specified index.
*
* @param index index.
*
* @return element.
*/
public Object getElementAt(int index)
{
return getThemes().get(index);
}
/**
* Returns the index-position of the specified object in the list.
*
* @param anObject object.
*
* @return an int representing the index position, where 0 is the first position
*/
public int getIndexOf(Object anObject)
{
return getThemes().indexOf(anObject);
}
}
}