package com.gmail.dpierron.calibre.gui; /** * Created by WalkerDJ on 19/04/2014. * * This class describes a single field within the GUI. * * It purpose is to allo some generalisation of the code to do with tranlating * the user interface, and loading and storing configuration settings. * * It uses the Java reflection API to allow method names to be derived from strings. Since * for all properties we have a get, set and isReadOnly method we can store just the base name. */ import com.gmail.dpierron.calibre.configuration.ConfigurationHolder; import com.gmail.dpierron.calibre.configuration.ConfigurationManager; import com.gmail.dpierron.tools.i18n.Localization; import com.gmail.dpierron.tools.Helper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.File; import javax.swing.*; import java.lang.reflect.*; import java.util.Locale; public class guiField { private final static Logger logger = LogManager.getLogger(guiField.class); private JComponent guiLabelField; // Label associated with this field private JComponent guiValueField; // Value field associated with this field. NULL if no value involved private String localizationKey; // LocalisationText (without .lable/.tooltip) private String methodBase; // The base of the methodNames associated with loading/storing values // Set it to null if this field does not have a value loaded/stored boolean negate; // For check boxes, set to true if checkbox value is negated when displayed int minimum, maximum; // Set for numeric fields to indicate range. If both zero then field is not nu // The following values are cached to reduce the overhead of deriving them each time private static ConfigurationHolder currentConfiguration = null; private Class paramTypes[] = new Class[1]; private Object profileType = null; private Object guiType = null; // // CONSTRUCTORS // Constructor for checkbox fields that have associated configuration information public guiField (JComponent label, JComponent value, String localizationKey, String methodBase, int minimum, int maximum) { initialise(label,value, localizationKey, methodBase, false, minimum,maximum); } // Constructor for checkbox fields that have associated configuration information public guiField (JComponent label, JComponent value, String localizationKey, String methodBase, boolean negate) { initialise(label,value, localizationKey, methodBase, negate, 0,0); } // Constructor for none-checkbox fields that have associated configuration information public guiField (JComponent label, JComponent value, String localizationKey, String methodBase) { initialise(label,value, localizationKey, methodBase, false, 0,0); } // Constructor for fields that have no associated configuration information public guiField (JComponent label, JComponent value, String localizationKey) { initialise(label,value, localizationKey, null, false, 0,0); } // METHODS private void initialise(JComponent label, JComponent value, String localizationKey, String methodBase,boolean negate, int minimum, int maximum) { this.guiLabelField = label; this.guiValueField = value; this.localizationKey = localizationKey; this.methodBase = methodBase; this.negate = negate; this.minimum = minimum; this.maximum = maximum; } public void setCurrentConfiguration(ConfigurationHolder c) { currentConfiguration = c; } /** * Apply the localization for the given field(s) */ public void translateTexts () { String labelText = Localization.Main.getText(localizationKey + ".label"); // Allow for cases where keyndoes not end in .label if (labelText.endsWith("label")) labelText = Localization.Main.getText(localizationKey); if (labelText.equals(localizationKey)) labelText = ""; String tooltipText = Localization.Main.getText(localizationKey + ".tooltip"); // Allow for tooltips not lways being supplied if (tooltipText.endsWith(".tooltip")) tooltipText = ""; // Apply localisation to the designated 'label' field if (guiLabelField != null) { if (guiLabelField instanceof JButton) { ((JButton) guiLabelField).setText(labelText); } else if (guiLabelField instanceof JLabel) { ((JLabel) guiLabelField).setText(labelText); } else if (guiLabelField instanceof JTextField) { ((JTextField) guiLabelField).setText(labelText); } else if (guiLabelField instanceof JCheckBox) { ((JCheckBox) guiLabelField).setText(labelText); } else if (guiLabelField instanceof JTabbedPane) { int tabIndex = -1 + (localizationKey.charAt(localizationKey.length() - 1)) - '0'; ((JTabbedPane) guiLabelField).setTitleAt(tabIndex, labelText); } else if (guiLabelField instanceof JMenuItem) { ((JMenuItem) guiLabelField).setText(labelText); } else { logger.error("setTranslateTexta: Cannot handle the type for LocalizationKey=" + localizationKey); return; } } // If tooltip provided apply tooltip localisation if (Helper.isNotNullOrEmpty(tooltipText)) { if (guiLabelField != null) guiLabelField.setToolTipText(tooltipText); if (guiValueField != null) guiValueField.setToolTipText(tooltipText); } } /** * Get the label field associated with this instance * * @return */ public JComponent getGuiLabel() { return guiLabelField; } public int getMinimum() { return minimum; } public int getMaximum () { return maximum; } private Integer getValue(String s) { try { int i = Integer.parseInt(s); return i; } catch (NumberFormatException e) { return -1; } } /** * Method that gets the value from the GUI and * returns it as an object of the correct type to * compare against a profile object. * * @return */ public Object getGuiValue() { if (methodBase == null) { return null; } Object guiValue = null; // Text fields if (guiValueField instanceof JTextField) { String s = ((JTextField) guiValueField).getText(); assert s != null : "getGuiValue: Unexpected null return reading value of " + guiValueField.getName(); if (minimum == 0 && maximum == 0) { if (String.class.equals(profileType)) { guiValue = s; } else if (File.class.equals(profileType)) { if (s.length() > 0) guiValue = new File(s); } else if (Integer.class.equals (profileType)) { guiValue = getValue(s); } else if (Locale.class.equals(profileType)) { guiValue = s.length() == 0 ? "en" : s; } else { logger.error("getGuiValue: Unexpected profileType for 'get" + methodBase + "', type=" + profileType.getClass().getName()); guiValue = null; } } else { assert (Integer.class.equals(profileType)); guiValue = getValue(s); } // Checkboxes } else if (guiValueField instanceof JCheckBox) { Boolean b = ((JCheckBox) guiValueField).isSelected(); guiValue = negate ? !b : b; // Combo boxes } else if (guiValueField instanceof JComboBox) { guiValue = ((JComboBox) guiValueField).getSelectedItem(); } else { logger.error("getGuiValue: object type (" + guiValueField.getClass().getName() + ") not recognised for " + guiValueField.getName()); guiValue = null; } // For language fields we need a locale object if (guiValue != null && Locale.class.equals(profileType)) { assert String.class.equals(guiValue.getClass()) : "Unexpected class " + guiValue.getClass().getName() +" (" + guiValue + ")"; guiValue = Helper.getLocaleFromLanguageString((String)guiValue); } return guiValue; } /** * Transfer the value to the correct GUI field * * @param oValue */ private void putGuiValue (Object oValue) { if (oValue == null) { if (profileType instanceof File) { oValue=""; } } // Text fields if (guiValueField instanceof JTextField) { String s; if (oValue instanceof String) { s = (String)oValue; } else if (oValue instanceof Integer) { s = ((Integer)oValue).toString(); } else if (oValue instanceof File) { s = ((File)oValue).getAbsolutePath(); } else if (oValue instanceof Locale) { s = ((Locale)oValue).getLanguage(); } else { // We assume an empty string for null being returned s = ""; } // If parameter exceeds maximum, then coerce to the maximum value if (minimum != 0 || maximum != 0) { if (Integer.parseInt(s) > maximum) { s = Integer.toString(maximum); } InputVerifier iv = new InputVerifier() { @Override public boolean verify(JComponent input) { if (!(input instanceof JTextField)) return false; try { Integer.parseInt(((JTextField) input).getText()); return true; } catch (NumberFormatException e) { return false; } } }; ((JTextField) guiValueField).setInputVerifier(iv); } ((JTextField) guiValueField).setText(s); // Check boxes } else if (guiValueField instanceof JCheckBox) { assert oValue instanceof Boolean; ((JCheckBox)guiValueField).setSelected(negate ? !(Boolean)oValue : (Boolean)oValue); } else if (guiValueField instanceof JComboBox) { if (oValue instanceof Locale) { ((JComboBox) guiValueField).setSelectedItem(((Locale) oValue).getLanguage()); } else { ((JComboBox) guiValueField).setSelectedItem((String) oValue); } } else { logger.error("loadValue: putGuiValue oBject type not recognised for " + guiLabelField); return; } } /** * Method that gets the correct profile value * * @return */ public Object getProfileValue() { Method profileGetMethod = getProfileGetMethod(); if (profileGetMethod == null) { return null; } Object profileValue; try { // Invoke the get method profileValue = profileGetMethod.invoke(ConfigurationManager.getCurrentProfile()); } catch (Exception e) { logger.error("loadValue: Invoke 'get" + methodBase + "' Exception " + e); profileValue = null; } return profileValue; } /** * Store a value in the profile * * @param setValue */ private void putProfileValue(Object setValue) { Method profileSetMethod = getProfileSetMethod(); if (profileSetMethod != null) { try { profileSetMethod.invoke(ConfigurationManager.getCurrentProfile(), setValue); // None of the following should happen except in development - but lets play safe! } catch (final Exception e) { logger.error("storeValue: invoke 'set" + methodBase + "Excepion " + e); } } } /** * Transfer a value from the GUI to the profile * Do nothing if field has no associated value field or method */ public void storeValue() { if (Helper.isNullOrEmpty(methodBase)) { // logger.trace("storeValue: Method baee name to store value not set"); return; } if (Helper.isNullOrEmpty(guiValueField)) { // logger.trace("storeValue: Attempt to store a null value"); return; } // Now we can set up the parameter for the setmethod Object setValue = getGuiValue(); if (setValue == null && ! (profileType instanceof File)) { return; } putProfileValue(setValue); } /** * Transfer a value from the profile to the GUI * Do nothing if field has no associated value field or method */ public void loadValue() { // Check if there is a GUI field to store any value if (Helper.isNullOrEmpty(guiValueField)) { return; } // Check if we have a profile method base specified if (Helper.isNullOrEmpty(methodBase)) { return; } // Check we can actually find the method Method method = getProfileGetMethod();; if (method == null) { return; } // Get the value from the profile // Only time null is valid for File objects Object loadValue = getProfileValue(); putGuiValue(loadValue); // Now use ReadOnly method to see if field should be currently dsiabled // If no such method eists, then assume is always enabled Boolean disable; try { String methodName = "is" + methodBase + "ReadOnly"; method = currentConfiguration.getClass().getDeclaredMethod(methodName); disable = (Boolean) method.invoke(currentConfiguration); } catch (final Exception e) { // We assume ReadOnly method does not exist if we get to this point. disable = false; } if (guiLabelField != null) guiLabelField.setEnabled(!disable); if (guiValueField != null) guiValueField.setEnabled(!disable); } /** * Get the method for getting a value from the profile * * If successful will also set up the global variables * paramTypes[] * profileType * This means you MUST have found getProfileGetMethod() before using these * * @return method if OK, and null otherwise */ private Method getProfileGetMethod() { Method profileGetMethod = null; String getMethodName = "get" + methodBase; try { // Use get method to work out parameter type for set method profileGetMethod = currentConfiguration.getClass().getDeclaredMethod(getMethodName); paramTypes[0] = profileGetMethod.getReturnType(); profileType = profileGetMethod.getReturnType(); } catch (NoSuchMethodException e1) { logger.error("getProfileGetMethod: unable to find method '" + getMethodName + "'"); profileGetMethod = null; paramTypes = null; profileType = null; } return profileGetMethod; } /** * Get the method for storing values in the profile. * * @return method if successful, null otherwise */ private Method getProfileSetMethod() { String setMethodName = "set" + methodBase; Method profileSetMethod = null; try { profileSetMethod = currentConfiguration.getClass().getDeclaredMethod(setMethodName, paramTypes); } catch (NoSuchMethodException e1) { logger.error("getProfileSetMethod: unable to find method '" + setMethodName + "'"); profileSetMethod = null; } return profileSetMethod; } }