/*
GNU General Public License
CacheWolf is a software for PocketPC, Win and Linux that
enables paperless caching.
It supports the sites geocaching.com and opencaching.de
Copyright (C) 2006 CacheWolf development team
See http://www.cachewolf.de/ for more information.
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; version 2 of the License.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package CacheWolf.utils;
/*
* CacheWolf - Local settings class
*
*/
import ewe.fx.Rect;
import ewe.io.File;
import ewe.io.FileBase;
import ewe.io.FileReader;
import ewe.io.IOException;
import ewe.io.TreeConfigFile;
import ewe.io.TreeConfigNode;
import ewe.sys.Double;
import ewe.sys.LocalResource;
import ewe.sys.Locale;
import ewe.sys.Long;
import ewe.sys.Vm;
import ewe.ui.SplittablePanel;
import ewe.ui.Window;
import ewe.ui.WindowConstants;
/**
* This class handles internationalisation and some other local stuff like
* decimal separator, screen dimensions etc.
*
* The methods are static, the class does not need initialisation.
*
* @author salzkammergut
* Changes:
* 20061122 Changed name to MyLocale. Added screen width & height, formatLong, SIP functions
* 20061124 Added formatDouble
*/
public class MyLocale {
/** This language used if the system language is not supported by CacheWolf */
private final static String standardLanguage = "EN";
/**
* must be set once before (first implicit) call of init()<br>
* get it from Preferences two Char language Code or AUTO
*/
public static String language = "EN";
private static Locale l = null;
private static LocalResource lr = null;
// size of maximized window
private static Rect screenSize = (Rect) Window.getGuiInfo(WindowConstants.INFO_SCREEN_RECT, null, new Rect(), 0);
public static String initErrors;
private static String getLocaleFileName(String languageshort) {
return FileBase.makePath(FileBase.getProgramDirectory(), "languages/" + languageshort.toUpperCase() + ".cfg");
}
/**
* This is needed because of 2 Bugs (not supporting French and inconsistant LocaleID in the ewe-VM v1.49
* For details see comments in method body
* [maybe one is inherited from windows: not supporting french]
*
* @param language_
* 2 digits of language code as specified in ISO
*/
private static void setLocale(String language_) {
String country = "";
if (language_.equalsIgnoreCase("DE"))
country = "DEU";
if (language_.equalsIgnoreCase("FR"))
country = "FRA";
if (language_.equalsIgnoreCase("EN"))
country = "USA";
if (language_.equalsIgnoreCase("NL"))
country = "NLD";
if (language_.equalsIgnoreCase("PL"))
country = "POL";
if (language_.equalsIgnoreCase("SV"))
country = "SWE";
int tmp = Locale.createID(language_, country, 0); // in ewe-vm v1.49 this call is enough to set the locale correctly and this works even with not supported languages like FR (french), e.g. it works even if tmp == -1, call new Locale() instead of new Locale(tmp) then.
tmp = (tmp >= 1024 ? tmp - 1024 : tmp); // ewe-vm v1.49 some times returns the correct value + 1024
if (tmp > -1)
l = new Locale(tmp);
else
l = Locale.createFor("EN", "", 0 /*Locale.FORCE_CREATION*/); // forcing the requiered language doesn't work, because Locale.numberformat and so on cannot determine the requested format then. BTW: if French is system language new Locale() works even in ewe-vm v1.49
resourcelanguage = language_;
}
/**
* This is used to determine the language file name - it is necessary because
* ewe-vm v1.49 doesn't support French
*/
static String resourcelanguage;
static boolean inInit = false;
private static void init() throws IllegalThreadStateException {
if (inInit) {
// this can happen, if ewe is loading another class in background, which causes a call to e.g. MyLocale.getDigSeperator (most likely in a static statement).
// Ewe-Vm v1.49 seems to be loading static classes ahead, causing the danger of this problem.
throw new IllegalThreadStateException("init may not be run twice");
}
inInit = true;
initErrors = "";
// the following logic priority: 1. try to use specified language (if specified), 2. try to use system language, 3. try to use english, 4. use hard coded messages
l = null;
if ((language.length() != 0) && (!language.equalsIgnoreCase("auto"))) { // Was a language explicitly specified?
setLocale(language);
if (!(new File(getLocaleFileName(resourcelanguage)).exists())) {
l = null; // language file not found
initErrors += "Language " + language + " not found - using system language\n";
// don't copy this into a language file, because it is only used if no languages file can be accessed
}
}
if (l == null) {
// no language specified OR specified language not available -> use system default
setLocale(Vm.getLocale().getString(Locale.LANGUAGE_SHORT, 0, 0));
// test if a localisation file for the system language exists
if (!(new File(getLocaleFileName(resourcelanguage)).exists())) {
setLocale(standardLanguage);
initErrors += "Your system language is not supported by cachewolf - using English\n You can choose a different language in the preferences\n";
}
}
lr = null;
if (new File(getLocaleFileName(resourcelanguage)).exists()) {
// ewe.io.TreeConfigFile.getConfigFile(getLocaleFileName(resourcelanguage));
// above replaced for to change resourcefile to have UTF-8 Codec
TreeConfigFile tcf = getConfigFile(getLocaleFileName(resourcelanguage));
if (tcf != null) {
lr = tcf.getLocalResourceObject(new Locale() {
public String getString(int what, int forValue, int options) {
// this is necessary because French cannot be set in ewe-vm v1.49
if (what == LANGUAGE_SHORT)
return resourcelanguage;
else
return super.getString(what, forValue, options);
}
}, "cachewolf.Languages");
}
}
if (lr == null) {
initErrors += "Language file " + getLocaleFileName(resourcelanguage) + " couldn't be loaded - using hard coded messages";
lr = new LocalResource() {
public Object get(int id, Object data) {
return data;
}
public Object get(String id, Object data) {
return data;
}
};
}
inInit = false;
}
/**
* Return a localized string
*
* The localized strings are stored in the configuration file (relative to
* executable:<br>
* _config/cachewolf.Languages.cfg
* If the configuration file does not exist or a string cannot be found in
* the file, the defaultValue is returned.
*
* @param resourceID
* The unique number of the resource
* @param defaultValue
* The default value of the string (if not found in the configuration file)
* @return The localized string
*/
public static String getMsg(int resourceID, String defaultValue) {
if (l == null)
init();
if (lr != null) {
String res;
res = (String) lr.get(resourceID, defaultValue);
if (res != null)
return res;
//Fallthrough to default value if string does not exist in file
}
return defaultValue;
}
/**
* Get the ISO two letter (lowercase) name of the locale language
*
* @return ISO two letter abbreviation of the locale language
*/
public static String getLocaleLanguage() {
if (l == null)
init();
return l.getString(Locale.LANGUAGE_SHORT, 0, 0);
}
/**
* Get the three letter (uppercase) ISO country code
*
* @return The three letter (uppercase) ISO country code
*/
public static String getLocaleCountry() {
if (l == null)
init();
return l.getString(Locale.COUNTRY_SHORT, 0, 0);
}
/**
* Formats a Long integer to a given format specifier
*
* @param number
* A Long which contains the number to be formatted
* @param fmt
* A string containing the format specification</br>
* '$' indicates that a currency symbol should be used. </br>
* ',' indicates that thousands groupings should be used. </br>
* '.' separates formatting before the decimal point and after the decimal point.</br>
* '0' before the decimal point indicates the number of digits before the decimal point.</br>
* @return The formatted number
*/
public static String formatLong(Long number, String fmt) {
if (l == null)
init();
return l.format(Locale.FORMAT_PARSE_NUMBER, number, fmt);
}
/**
* Formats a long to a given format specifier
*
* @param number
* A long containing the number to be formatted
* @param fmt
* A string containing the format specification</br>
* @return The formatted number
*/
public static String formatLong(long number, String fmt) {
Long L = new Long();
L.set(number);
return formatLong(L, fmt);
}
/**
* Formats a Double to a given format specifier
*
* @param number
* A Double containing the number to be formatted
* @param fmt
* A string containing the format specification</br>
* @return The formatted number
*/
public static String formatDouble(ewe.sys.Double number, String fmt) {
if (l == null)
init();
return l.format(Locale.FORMAT_PARSE_NUMBER, number, fmt);
}
/**
* Formats a Double to a given format specifier
*
* @param number
* A double containing the number to be formatted
* @param fmt
* A string containing the format specification</br>
* @return The formatted number
*/
public static String formatDouble(double number, String fmt) {
Double d = new Double();
d.set(number);
return formatDouble(d, fmt);
}
/**
* This function checks whether the device supports a
* supplementary input panel (SIP) and if yes, shows it.
*
*/
public static void setSIPOn() {
if (Vm.isMobile()) {
Vm.setSIP(Vm.SIP_ON);
}
}
/**
* This function checks whether the device supports a
* supplementary input panel (SIP) and if yes, hides it and
* also hides the button.
*
*/
public static void setSIPOff() {
if (Vm.isMobile()) {
Vm.setSIP(0);
}
}
/**
* This function checks whether the device supports a
* supplementary input panel (SIP) and if yes, hides it and just
* shows the button.
*
*/
public static void setSIPButton() {
if (Vm.isMobile()) {
Vm.setSIP(Vm.SIP_LEAVE_BUTTON);
}
}
/**
* This method modifies the thickness of splitter on modern PDAs with a VGA resolution or better.
*
* @param split
*/
public static void setSplitterSize(SplittablePanel split) {
if (Vm.isMobile() && screenSize.height > 400) {
split.theSplitter.thickness = 20;
}
}
/**
* Returns the path to a localized version of a help file
*
* @param basename
* @return
*/
public static String getLocalizedFile(String basename) {
String lang = MyLocale.getLocaleLanguage();
String programmDirectory = FileBase.getProgramDirectory() + "/";
int index = basename.lastIndexOf('.');
String prefix = basename.substring(0, index);
String suffix = basename.substring(index);
File f = new File(programmDirectory, prefix + '_' + lang + suffix);
if (f.exists()) {
return f.toString();
} else {
return new File(programmDirectory, basename).toString();
}
}
//===================================================================
static TreeConfigFile getConfigFile(String path)
//===================================================================
{
try {
ewe.io.TreeConfigFile tcf = new TreeConfigFile();
tcf.configFileName = path;
TreeConfigNode root = (TreeConfigNode) tcf.getRoot();
FileReader r = null;
r = new FileReader(path);
r.codec = new ewe.io.JavaUtf8Codec();
r.read(); // should skip the UTF-8 BOM first 3 Bytes, but doesn't
TreeConfigNode tcn = root;
while (true) {
String got = r.readLine();
if (got == null)
break;
got = got.trim();
if (got.startsWith(";")) {
continue;
}
if (got.startsWith("#")) {
continue;
}
if (got.startsWith("{..}")) {
if (tcn == root)
break;
else
tcn = (TreeConfigNode) tcn.getParent();
continue;
}
if (got.startsWith("{")) {
int idx = got.indexOf('}');
if (idx == -1)
idx = got.length();
TreeConfigNode nn = new TreeConfigNode(got.substring(1, idx));
tcn.addChild(nn);
tcn = nn;
continue;
}
int eq = got.indexOf(',');
if (eq != -1) {
// split at first ,
String key = got.substring(0, eq).trim();
String value = got.substring(eq + 1).trim();
// remove surrounding "
value = value.substring(1, value.length() - 1);
// handle escaped " (\"), simplifying copy+paste to/from java/eclipse
value = STRreplace.replace(value, "\\\"", "\"");
// handle newLine (\n)
value = STRreplace.replace(value, "\\n", "\n");
tcn.getProperties().add(key, value);
}
}
r.close();
return tcf;
} catch (IOException e) {
return null;
}
}
}