Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.ku.brc.ui; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Stack; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.plaf.FontUIResource; import javax.swing.text.DefaultEditorKit; import javax.swing.text.JTextComponent; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import edu.ku.brc.af.ui.ViewBasedDialogFactoryIFace; import edu.ku.brc.af.ui.forms.validation.ValTextField; import edu.ku.brc.exceptions.UIException; import edu.ku.brc.ui.dnd.GhostGlassPane; import edu.ku.brc.ui.dnd.SimpleGlassPane; import edu.ku.brc.ui.tmanfe.SearchReplacePanel; import edu.ku.brc.util.FileCache; /** * This class manages all things UI. It is the central clearing house for UI components. * Meaning you can register various UI components by name and then get them later.<br><br> * <br> * Using the <i>getMostRecentFrame</i> method<br> * You <i>SHOULD</i> set the RECENTFRAME ui component when you are in a dialog and then any error dialogs * can be parented to your dialog. But if you set it you MUST set it back to null. (NOte if you forget it will always return * the TOPFRAME, but don't rely on this) * @code_status Complete ** * @author rods * */ public class UIRegistry { // Static Data Members protected static final String MISSING_FACTORY_MSG = "The object has not been set for the ViewBasedDialogFactoryIFace. This class can be used without first setting a factory implementing this interface."; protected static final String EMBEDDED_DB_PATH = "embedded.dbpath"; protected static final String MOBILE_EMBEDDED_DB_PATH = "mobile.embedded.dbpath"; protected static final String EMBEDDED_DB_DIR = "SPECIFY_DATA"; protected static final boolean debugPaths = false; public static final String FRAME = "frame"; public static final String MENUBAR = "menubar"; public static final String TOOLBAR = "toolbar"; public static final String GLASSPANE = "glasspane"; public static final String MAINPANE = "mainpane"; public static final String RECENTFRAME = "recentframe"; // Standard Actions public static final String UNDO = "Undo"; public static final String REDO = "Redo"; public static final String CUT = "Cut"; public static final String COPY = "Copy"; public static final String PASTE = "Paste"; public static final String FIND = "Find"; public static final String INSERT = "Insert"; public static final String ADD = "Add"; public static final String DELETE = "Delete"; public static final String Clear = "Clear"; public static final String LONGTERM_CACHE_MAP = "longterm-cache-map.xml"; private static final Logger log = Logger.getLogger(UIRegistry.class); protected static final UIRegistry instance = new UIRegistry(); protected static Rectangle frameRect = null; protected static GhostGlassPane oldGlassPane = null; protected static boolean showingGlassPane = false; protected static boolean isRelease = false; protected static boolean isTesting = false; protected static int STD_WAIT_TIME = 2000; // 2 Seconds public static int STD_FONT_SIZE = 20; // 20 point size protected static Boolean isEmbedded = null; protected static Boolean isMobile = null; // Data Members protected HashMap<String, Component> components = new HashMap<String, Component>(); protected Window topWindow = null; protected Stack<Window> windowStack = new Stack<Window>(); protected HashMap<String, HashMap<String, JComponent>> uiItems = new HashMap<String, HashMap<String, JComponent>>(); protected Font baseFont = null; protected Font defaultFont = null; protected FileCache longTermCache = null; protected FileCache shortTermCache = null; protected FileCache formsCache = null; protected JStatusBar statusBar = null; protected String defaultWorkingPath = null; protected String userDataDir = null; protected String appDataDir = null; protected String appName = null; // Resource Management protected ResourceBundle resourceBundle = null; protected Stack<ResBundleInfo> resBundleStack = new Stack<ResBundleInfo>(); protected String resourceName = "resources"; protected Locale resourceLocale = Locale.getDefault(); protected Locale platformLocale = Locale.getDefault(); protected static boolean doShowAllResStrErors = false; protected ViewBasedDialogFactoryIFace viewbasedFactory = null; protected HashMap<String, Action> actionMap = new HashMap<String, Action>(); //------------------------------------------------ // Undo / Redo Helpers //------------------------------------------------ protected static UndoAction undoAction; // these three are special protected static RedoAction redoAction; protected static LaunchFindReplaceAction launchFindReplaceAction; protected HashMap<Object, Action> actions = null; // XXX Doing the whole permanentFocusOwner owner thing is a kludge until // we really understand the right way to do Cut Copy Paste protected static Component permanentFocusOwner = null; static { // Insurance for non-English Locales try { ResourceBundle.getBundle("resources", Locale.getDefault()); //$NON-NLS-1$ } catch (MissingResourceException ex) { Locale.setDefault(Locale.ENGLISH); UIRegistry.setResourceLocale(Locale.ENGLISH); } instance.baseFont = new JLabel("").getFont(); instance.baseFont = instance.baseFont.deriveFont(Font.PLAIN); final KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); focusManager.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String propName = e.getPropertyName(); if (propName.equals("permanentFocusOwner")) { permanentFocusOwner = focusManager.getFocusOwner(); } /* System.out.println(propName+" "+ ( focusManager.getFocusOwner() != null ? focusManager.getFocusOwner().hashCode():"")+ " FO: ["+(focusManager.getFocusOwner() != null ? focusManager.getFocusOwner().getClass().getSimpleName() : "NULL")+"]"+ " PERM: ["+(permanentFocusOwner != null ? permanentFocusOwner.getClass().getSimpleName() : "NULL")+"]");//+" perm: "+permanentFocusOwner); if (focusManager.getFocusOwner() instanceof GetSetValueIFace) { System.out.println("-> "+((GetSetValueIFace)focusManager.getFocusOwner()).getValue()); } */ if (("focusOwner".equals(propName)) && undoAction != null && redoAction != null) { if (focusManager.getFocusOwner() instanceof UndoableTextIFace) { //System.out.println("Owner"); UndoableTextIFace undoableText = (UndoableTextIFace) focusManager.getFocusOwner(); if (undoableText != null) { //System.err.println("Hooking up undo manager for "+undoableText); undoAction.setUndoManager(undoableText.getUndoManager()); redoAction.setUndoManager(undoableText.getUndoManager()); } } else if (focusManager.getFocusOwner() instanceof JTextComponent) { undoAction.setUndoManager(null); redoAction.setUndoManager(null); } undoAction.updateUndoState(); redoAction.updateRedoState(); } } }); } /** * Default private constructor for singleton. * */ private UIRegistry() { if (resourceBundle == null) { resourceBundle = getResourceBundle(resourceName); } } /** * Returns the singleton. * @return the singleton instance */ public static UIRegistry getInstance() { return instance; } /** * @return the string for the current version number from the resources file (e.g. 6.2.10) */ public static String getAppVersion() { return getResourceString("SPECIFY_VERSION"); } /** * @return true if the install version number matches the jar version number. */ public static boolean doesAppversionsMatch() { String install4J = UIHelper.getInstall4JInstallString(); String resAppVersion = getAppVersion(); if (StringUtils.isEmpty(install4J) || StringUtils.isEmpty(resAppVersion) || !resAppVersion.equals(install4J)) { showLocalizedError("APPVER_MISMATCH", install4J, resAppVersion); return false; } return true; } /** * @return the platformLocale */ public static Locale getPlatformLocale() { return instance.platformLocale; } /* * If, to handle I18N issues with java loading of properties files, we load the properties files 'manually', * the following 2 methods could be used. */ /** * * @param name * @param locale * @return */ /*private static String getResourcePropertiesFileName(final String name, final Locale locale) { return "C:\\workspace\\XSpTrnk\\src\\" + name + "_" + locale.getLanguage() + ".properties"; }*/ /** * @param name * @param locale * @return */ /*public static ResourceBundle getPropertyResourceBundleFromFileinConfigDir(final String name, final Locale locale) { try { String resFileName = getResourcePropertiesFileName(name, locale); return new PropertyResourceBundle(new InputStreamReader(new FileInputStream(new File(resFileName)), "UTF-8")); } catch (Exception ex) { ex.printStackTrace(); return ResourceBundle.getBundle(name); } }*/ /** * Loads and returns a resource Bundle. * @param name the name of the Bundle * @return the resource bundle object */ public static ResourceBundle getResourceBundle(final String name) { ResourceBundle resBundle = null; try { // I know this seems like an odd think to check, // but I want it to initialize itself at start up and also be able to set a new on later. // this was the only way. if (instance == null || instance.resourceLocale == null) { resBundle = ResourceBundle.getBundle(name, new UTF8Control()); /* If properties files are stored in config dir and not loaded as resources: resBundle = getPropertyResourceBundleFromFileinConfigDir(name, Locale.getDefault()); }*/ } else { try { resBundle = ResourceBundle.getBundle(name, instance.resourceLocale, new UTF8Control()); /* if properties are stored in config dir and not loaded as resources: resBundle = getPropertyResourceBundleFromFileinConfigDir(name, instance.resourceLocale); */ } catch (MissingResourceException ex) { resBundle = ResourceBundle.getBundle(name, Locale.ENGLISH, new UTF8Control()); } } } catch (MissingResourceException ex) { log.error("Couldn't find Resource Bundle Name[" + name + "]", ex); } return resBundle; } /** * @return the resourceLocale */ public Locale getResourceLocale() { return resourceLocale; } /** * @param resourceLocale the resourceLocale to set */ public static void setResourceLocale(Locale resourceLocale) { if (!instance.resourceLocale.equals(resourceLocale)) { instance.resourceLocale = resourceLocale; instance.resourceBundle = getResourceBundle(instance.resourceName); } } /** * Pushes the Resource Info onto the stack (internal because of the 'new') * @param name the name of the resource * @param rb the resource bundle * @return the same res bundle */ protected ResourceBundle pushInternal(final String name, final ResourceBundle rb) { resBundleStack.push(new ResBundleInfo(name, rb)); return rb; } /** * Pushes the Resource Info onto the stack. * @param name the name of the resource * @param rb the resource bundle * @return the same res bundle */ public static ResourceBundle push(final String name, final ResourceBundle rb) { instance.pushInternal(instance.resourceName, instance.resourceBundle); instance.resourceBundle = rb; instance.resourceName = name; return rb; } /** * Loads a Resource Bundle by name and pushes it onto the Res bundle stack. * @param resName the name of the res bundle to load. * @return the loaded resource bundle or null if not found */ public static ResourceBundle loadAndPushResourceBundle(final String resName) { ResourceBundle rb = getResourceBundle(resName); if (rb != null) { push(resName, rb); } else { log.error("Unable to load ResourceBundle[" + resName + "]"); } return rb; } /** * Pops a Resource Bundle off the stack. * @return the res bundle */ public static ResourceBundle popResourceBundle() { if (instance.resBundleStack.size() > 0) { ResBundleInfo rbi = instance.resBundleStack.pop(); instance.resourceBundle = rbi.getResBundle(); instance.resourceName = rbi.getName(); } else { log.error("Tried to pop empty ResourceBundle Stack"); } return instance.resourceBundle; } /** * Return the current ResourceBundle. * @return the current ResourceBundle */ protected static ResourceBundle getResourceBundleInternal() { return instance.getResourceBundle(); } /** * Returns the main ResourceBundle. * @return Returns the main ResourceBundle */ public ResourceBundle getResourceBundle() { return resourceBundle; } /** * Returns the reourceName. * @return Returns the reourceName. */ public String getResourceName() { return resourceName; } /** * Sets the resource name. * @param resourceName The reourceName to set. */ public void setResourceName(final String resourceName) { this.resourceName = resourceName; } /** * Returns a localized string from the resource bundle (masks the thrown exception). * @param key the key to look up * @return Returns a localized string from the resource bundle */ protected String getResourceStringInternal(final String key) { try { //log.error("["+key+"]["+resourceBundle.getString(key)+"]"); if (StringUtils.isEmpty(key)) { log.warn("The key [" + key + "] was null/empty for localization"); return ""; } return resourceBundle.getString(key); } catch (MissingResourceException ex) { if (resBundleStack.size() == 0 || doShowAllResStrErors) { log.warn("Couldn't find key[" + key + "] in resource bundle [" + resourceName + "]"); } for (int i = resBundleStack.size() - 1; i > -1; i--) { ResBundleInfo ri = resBundleStack.elementAt(i); try { return ri.getResBundle().getString(key); } catch (MissingResourceException mre) { if (i == 0 || doShowAllResStrErors) { //log.error("Couldn't find key["+key+"] in resource bundle ["+ri.getName()+"]"); } } } return key; } } /** * This will enable the showing of all getResourceString ResourceBundle look up errors no matter * the level. Otherwise it only shows the error when it is not found at all. * @param doShowAllResStrErors the doShowAllResStrErors to set */ public static void setDoShowAllResStrErors(boolean doShowAllResStrErors) { UIRegistry.doShowAllResStrErors = doShowAllResStrErors; } /** * Returns a localized string from the resource bundle (masks the thrown exception). * @param key the key to look up * @return Returns a localized string from the resource bundle */ public static String getResourceString(final String key) { return instance.getResourceStringInternal(key); } /** * Returns a localized string from the resource bundle that has a format. * @param key the key to look up * @param params the object to be inserted into the string * @return Returns a localized string from the resource bundle */ public static String getFormattedResStr(final String key, final Object... params) { return String.format(instance.getResourceStringInternal(key), params); } /** * @return the getPermanentFocusOwner */ public static Component getPermanentFocusOwner() { return permanentFocusOwner; } /** * Returns whether this is a release. * @return whether this is a release. */ public static boolean isRelease() { return isRelease; } /** * Sets whether this is a release. * @param isRelease the isRelease to set */ public static void setRelease(boolean isRelease) { UIRegistry.isRelease = isRelease; } /** * @return the isTesting */ public static boolean isTesting() { return isTesting; } /** * @param isTesting the isTesting to set */ public static void setTesting(boolean isTesting) { UIRegistry.isTesting = isTesting; } /** * @return the statusBar */ public static JStatusBar getStatusBar() { return instance.statusBar; } /** * @param statusBar the statusBar to set */ public static void setStatusBar(JStatusBar statusBar) { instance.statusBar = statusBar; } /** * Set the working directory. It is not recommended to use this because the working directory will automatically be created. * @param defaultWorkingPath the new and different working directory. */ public static void setDefaultWorkingPath(final String defaultWorkingPath) { File path = new File(defaultWorkingPath); String defPath = defaultWorkingPath; try { defPath = path.getCanonicalPath(); } catch (IOException e) { } dumpCanonicalPath("setDefaultWorkingPath", defPath); instance.defaultWorkingPath = defPath; } /** * Returns the "working" directory which is platform specific. It will create one if one is not created * <b>NOTE: The application name must be set first.</b><br> * One Windows it is ...\<i><user name></i>\Application Data\<application name><br> * On Unix based platforms it will create a directory of the "application name" with a "." in front. * For example: <code>/home/john/.specify</code> * @return Returns the "working" directory which is platform specific. */ public static String getDefaultWorkingPath() { if (instance.defaultWorkingPath == null) { File file = new File("."); instance.defaultWorkingPath = UIHelper.stripSubDirs(file.getAbsolutePath(), 1); log.debug("Working Path not set, setting it to[" + instance.defaultWorkingPath + "]"); } //log.debug("Def Working Path["+instance.defaultWorkingPath+"]"); if (debugPaths) { try { log.debug("************************ getDefaultWorkingPath: Canonical[" + (new File(instance.defaultWorkingPath).getCanonicalPath()) + "]"); } catch (Exception ex) { } } return instance.defaultWorkingPath; } /** * Creates a File object for a Sub-Directory inside the Default Working Path Directory. * If the directory doesn't exist you can have it created. * @param dirName the new or existing directory name * @param createIt true to create it if it doesn't exist, false doesn't create it * @return a File object to the the directory */ public static File getAppDataSubDir(final String dirName, final boolean createIt) { File newDir = new File(getAppDataDir() + File.separator + dirName); if (!newDir.exists() && createIt) { if (!newDir.mkdirs()) { return null; } } return newDir; } /** * @param appDataDir */ public static void setBaseAppDataDir(final String appDataDir) { dumpCanonicalPath("setBaseAppDataDir", appDataDir); instance.appDataDir = appDataDir; } /** * This method will create the "working" directory for the application. * @return the the working directory where local preferences and other files are saved. */ public static String getAppDataDir() { File dir; //log.debug("1 AppDataDir["+instance.appDataDir+"]"); if (instance.appDataDir == null) { dir = new File(getUserHomeAppDir()); } else { //log.debug("2 AppDataDir["+instance.appDataDir+"]"); if (instance.appDataDir.equals(".")) { //log.debug("************* dot"); dir = new File(UIHelper.stripSubDirs((new File(".").getAbsolutePath()), 1) + File.separator + instance.appName); } else { //log.debug("3 AppDataDir["+instance.appDataDir+"]"); dir = new File(instance.appDataDir + File.separator + instance.appName); } } //log.debug("AppDataDir["+dir.getAbsolutePath()+"]"); if (!dir.exists()) { if (!dir.mkdir()) { throw new RuntimeException("Couldn't create data directory for " + instance.appName + " [" + dir.getAbsolutePath() + "]"); } } if (debugPaths) { try { log.debug("************************ setDefaultWorkingPath: [" + dir.getCanonicalPath() + "]"); } catch (Exception ex) { } } try { return dir.getCanonicalPath(); } catch (IOException ex) { ex.printStackTrace(); } return dir.getAbsolutePath(); } /** * Gets the location of Application directory by calling {@link getUserHomeDir()} that is platform specific and requires the "application name" be set first. * @return the string to a platform specify user data directory for the application name. */ public static String getUserHomeAppDir() { assert (instance.appName == null); return getUserHomeDir() + File.separator + instance.appName; } /** * Get the "user" based working directory that is platform specific. * @return the string to a platform specify user data directory */ /** * Get the "user" based working directory that is platform specific. When in Mobile 'mode' * it returns the DefaultWorkingPath, when in Standard app mode it uses the 'Default User Home Dir" * which is the platform specific true home directory. * @return the string to a platform specify user data directory */ public static String getUserHomeDir() { //log.error("isMobile() "+isMobile()+"["+UIRegistry.getDefaultWorkingPath()+"]"); return isMobile() ? UIRegistry.getDefaultWorkingPath() : getDefaultUserHomeDir(); } /** * @return */ public static String getDefaultUserHomeDir() { String homeDir = System.getProperty("user.home"); UIHelper.OSTYPE osType = UIHelper.getOSType(); if (osType == UIHelper.OSTYPE.Windows) { String appDataLoc = System.getenv("LOCALAPPDATA"); return StringUtils.isNotEmpty(appDataLoc) ? appDataLoc : homeDir; } else if (osType == UIHelper.OSTYPE.MacOSX) { String docPath = homeDir + File.separator + "Documents"; // Not Localized if (new File(docPath).exists()) { return docPath; } } // else return homeDir; } /** * */ public static void dumpPaths() { String mobile = ""; try { if (StringUtils.isNotEmpty(getMobileEmbeddedDBPath())) { mobile = (new File(getMobileEmbeddedDBPath())).getCanonicalPath(); } } catch (IOException ex) { } if (debugPaths) { log.debug("AppDataDir: " + getAppDataDir()); log.debug("UserHomeAppDir: " + getUserHomeAppDir()); log.debug("UserHomeDir: " + getUserHomeDir()); log.debug("DefaultEmbeddedDBPath: " + getDefaultEmbeddedDBPath()); log.debug("DefaultMobileEmbeddedDBPath: " + getEmbeddedDBPath()); log.debug("MobileEmbeddedDBPath: " + mobile); log.debug("DefaultWorkingPath: " + getDefaultWorkingPath()); //log.debug("MobileMachineDir: "+DBConnection.getMobileMachineDir("<database name>")); } } /** * Returns the current application name. * @return the current application name. */ public static String getAppName() { return instance.appName; } /** * Sets the application name and this name cannot be changed (meaning it can only be set once). * @param appName the application name (it is best if it doesn't have a space in the middle) */ public static void setAppName(final String appName) { if (StringUtils.isNotEmpty(appName) && StringUtils.isEmpty(instance.appName)) { instance.appName = appName; } else { throw new RuntimeException("You cannot set the app name twice or with an empty string!"); } } /** * Registers a uicomp. * @param category the category to be registered * @param name the name * @param uiComp the ui component * @throws UIException throws exception if it is already registered */ public static void registerUI(final String category, final String name, final JComponent uiComp) throws UIException { HashMap<String, JComponent> compsHash = instance.uiItems.get(category); if (compsHash == null) { compsHash = new HashMap<String, JComponent>(); instance.uiItems.put(category, compsHash); } if (compsHash.containsKey(name)) { throw new UIException( "UI component with Name[" + name + "] has already been registered to [" + category + "]."); } compsHash.put(name, uiComp); } /** * Unregisters a uicomp. * @param category the category to be registered * @param name the name * @throws UIException throws exception if it is not registered */ public static void unregisterUI(final String category, final String name) throws UIException { HashMap<String, JComponent> compsHash = instance.uiItems.get(category); if (compsHash == null) { throw new UIException("Couldn't find UI Category with Name[" + category + "]."); } JComponent comp = compsHash.get(name); if (comp == null) { throw new UIException("Couldn't find UI component with Name[" + name + "]."); } compsHash.remove(comp); } /** * Returns a UI component by name. * @param name the name of the component to be retrieved * @return a UI component by name */ public static Component get(final String name) { return instance.components.get(name); } /** * Returns a UI component by name. * @param altName the name of the component to be retrieved * @return a UI component by name */ public static Window getTopWindow() { return instance.topWindow; } public static void setTopWindow(final Window window) { instance.topWindow = window; } /** * Returns the most recent frame to be used, but note that there is no magic here. You * must set the the most recent frame in order for it to be used by someone else. The one * thing it does do for you, is that if you forgot to set it and someone else uses it it does * check to make sure it is not null AND visible. If either of these are true then it returns the TOPFRAME. * * @return Returns the most recent frame to be used, but note that there is no magic here. You * must set the the most recent frame in order for it to be used by someone else. The one * thing it does do for you, is that if you forgot to set it and someone else uses it it does * check to make sure it is not null AND visible. If either of these are true then it returns the TOPFRAME. */ public static Window getMostRecentWindow() { Window recent = instance.windowStack.size() > 0 ? instance.windowStack.peek() : null; return recent == null || !recent.isVisible() ? instance.topWindow : recent; } /** * Pushes Window onto the Stack. * @param window pushed onto the Window stack */ public static void pushWindow(final Window window) { instance.windowStack.push(window); } /** * Pops Window off the Stack. * @param window the window * @return pops window off the window stack */ public static Window popWindow(final Window requester) { if (instance.windowStack.size() > 0) { int index = instance.windowStack.indexOf(requester); if (index == -1) { log.error("Trying to pop Window not on the stack "); } else if (index < instance.windowStack.size() - 1) { log.error("Popping Window lower on Window stack than the Top [" + (instance.windowStack.size() - 1) + "] poping index[" + index + "]"); // Now Pop other windows so it does get out of wack while (instance.windowStack.peek() != requester) { instance.windowStack.pop(); } } else { return instance.windowStack.pop(); } } else { log.error("Trying to pop Window off an empty stack."); } return null; } /** * Formats an Internationalized string with a variable argument list. * @param key the I18N key * @param args the list args * @return a formatted string */ public static String getLocalizedMessage(final String key, final Object... args) { return String.format(getResourceString(key), args); } /** * Returns the ViewBasedFacory for the application. * @return the ViewBasedFacory for the application */ public static ViewBasedDialogFactoryIFace getViewbasedFactory() { if (instance.viewbasedFactory == null) { String className = System.getProperty("edu.ku.brc.ui.ViewBasedDialogFactoryIFace", null); if (StringUtils.isNotEmpty(className)) { try { instance.viewbasedFactory = (ViewBasedDialogFactoryIFace) Class.forName(className) .newInstance(); } catch (Exception e) { InternalError error = new InternalError( "Can't instantiate ViewBasedDialogFactoryIFace factory " + className); error.initCause(e); throw error; } } else { throw new InternalError(MISSING_FACTORY_MSG); } } return instance.viewbasedFactory; } /** * Displays a dialog dialog with a parameterized localized message. * @param iconType the type of icon to use (Question, Error, etc) * @param titleKey the title localize key * @param msgKey the message localize key * @param args any args */ public static void showLocalizedMsg(final int iconType, final String titleKey, final String msgKey, final Object... args) { JOptionPane.showMessageDialog(UIRegistry.getTopWindow(), (args.length == 0 ? getResourceString(msgKey) : String.format(getResourceString(msgKey), args)), getResourceString(StringUtils.isNotEmpty(titleKey) ? titleKey : "WARNING"), iconType); } /** * Displays a Warning dialog with a non-localized error message. * @param titleKey the title localize key * @param msgKey the message localize key * @param args any args */ public static void showLocalizedMsg(final String msgKey) { showLocalizedMsg(JOptionPane.WARNING_MESSAGE, "WARNING", msgKey); } /** * Displays a Warning dialog with a non-localized error message. * @param titleKey the title localize key * @param msgKey the message localize key * @param args any args */ public static void showLocalizedMsg(final String titleKey, final String msgKey, final Object... args) { showLocalizedMsg(JOptionPane.WARNING_MESSAGE, titleKey, msgKey, args); } /** * Asks Yes or No question using a JOptionPane * @param yesKey the resource key for the Yes button * @param noKey the resource key for the No button * @param nonL10NMsg the message or question NOT Localized * @param titleKey the resource key for the Dialog Title * @return JOptionPane.NO_OPTION or JOptionPane.YES_OPTION */ public static int askYesNoLocalized(final String yesKey, final String noKey, final String nonL10NMsg, final String titleKey) { int userChoice = JOptionPane.NO_OPTION; Object[] options = { getResourceString(yesKey), getResourceString(noKey) }; userChoice = JOptionPane.showOptionDialog(UIRegistry.getMostRecentWindow(), nonL10NMsg, getResourceString(titleKey), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); return userChoice; } /** * Asks Yes, No, Cancel question using a JOptionPane * @param yesKey the resource key for the Yes button * @param noKey the resource key for the No button * @param cancelKey the resource key for the Cancel button * @param nonL10NMsg the message or question NOT Localized * @param titleKey the resource key for the Dialog Title * @return JOptionPane.NO_OPTION or JOptionPane.YES_OPTION */ public static int askYesNoLocalized(final String yesKey, final String noKey, final String cancelKey, final String nonL10NMsg, final String titleKey) { int userChoice = JOptionPane.CANCEL_OPTION; Object[] options = { getResourceString(yesKey), getResourceString(noKey), getResourceString(cancelKey) }; userChoice = JOptionPane.showOptionDialog(UIRegistry.getMostRecentWindow(), nonL10NMsg, getResourceString(titleKey), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); return userChoice; } /** * Displays a dialog with a text field. * @param lblKey the L10N key for the text field label * @param titleKey the L10N key for the title * @param msgKey optional message to be displayed, can be null or empty string * @param doMustHaveValue indicates that the dialog will only have an OK and will no close until a value is entered. * @return the string entered into the dialog. */ public static String askForString(final String lblKey, final String titleKey, final String msgKey, final boolean doMustHaveValue) { CellConstraints cc = new CellConstraints(); PanelBuilder pb = new PanelBuilder( new FormLayout("p,2px,f:p:g", "p" + (StringUtils.isNotEmpty(msgKey) ? ",4px,p:g" : ""))); final ValTextField vt = new ValTextField(32); vt.setRequired(doMustHaveValue); pb.add(UIHelper.createI18NFormLabel(lblKey), cc.xy(1, 1)); pb.add(vt, cc.xy(3, 1)); if (StringUtils.isNotEmpty(msgKey)) { pb.add(UIHelper.createI18NLabel(msgKey), cc.xyw(1, 3, 3)); } pb.setDefaultDialogBorder(); final CustomDialog dlg = new CustomDialog((Frame) null, getResourceString(titleKey), true, doMustHaveValue ? CustomDialog.OK_BTN : CustomDialog.OKCANCEL, pb.getPanel()); dlg.createUI(); dlg.getOkBtn().setEnabled(false); vt.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { dlg.getOkBtn().setEnabled(StringUtils.isNotEmpty(vt.getText())); } }); UIHelper.centerAndShow(dlg); return vt.getText(); } /** * Displays an error dialog with a non-localized error message. * @param msg the message */ public static void showError(final String msg) { showError(null, msg); } /** * Displays an error dialog with a non-localized error message. * @param dlgType Dialog type error or warning * @param msg the message */ public static void showError(final Integer dlgType, final String msg) { log.error(msg); String titleKey = dlgType != null && dlgType == JOptionPane.WARNING_MESSAGE ? "WARNING" : "UIRegistry.UNRECOVERABLE_ERROR_TITLE"; JOptionPane.showMessageDialog(UIRegistry.getTopWindow(), msg, getResourceString(titleKey), dlgType == null ? JOptionPane.ERROR_MESSAGE : dlgType); } /** * Displays an error dialog with a non-localized error message in a non-modal dialog. * @param msg the message */ public static void showErrorNonModal(final String msg) { log.error(msg); CellConstraints cc = new CellConstraints(); PanelBuilder pb = new PanelBuilder(new FormLayout("f:p:g", "f:p:g")); pb.add(UIHelper.createLabel(msg), cc.xy(1, 1)); pb.setDefaultDialogBorder(); CustomDialog dlg = new CustomDialog((Frame) UIRegistry.getTopWindow(), getResourceString("UIRegistry.UNRECOVERABLE_ERROR_TITLE"), false, CustomDialog.OK_BTN, pb.getPanel()); dlg.setVisible(true); } /** * Displays an error dialog with a localized error message. * @param key the bundle key for the message */ public static void showLocalizedError(final String key) { showError(null, getResourceString(key)); } /** * Displays an error dialog with a localized error message. * @param dlgType Dialog type error or warning * @param key the bundle key for the message */ public static void showLocalizedError(final Integer dlgType, final String key) { showError(dlgType, getResourceString(key)); } /** * Displays an error dialog with a localized error message. * @param key the bundle key for the message * @param args args for the message */ public static void showLocalizedError(final String key, final Object... args) { showError(String.format(getResourceString(key), args)); } /** * Displays an error dialog with a localized error message. * @param dlgType Dialog type error or warning * @param key the bundle key for the message * @param args args for the message */ public static void showLocalizedError(final Integer dlgType, final String key, final Object... args) { showError(dlgType, String.format(getResourceString(key), args)); } /** * Sets the ViewBasedFacory for the application. * @param viewbasedFactory the factory */ /*public static void setViewbasedFactory(ViewBasedDialogFactoryIFace viewbasedFactory) { instance.viewbasedFactory = viewbasedFactory; }*/ /** * Registers a uiComp into the applications. * @param name the name of the UI component to be registered * @param uiComp the UI component to be registered */ public static void register(final String name, final Component uiComp) { if (uiComp != null) { if (instance.components.get(name) == null) { instance.components.put(name, uiComp); } else { throw new RuntimeException("Registering a uiComp with an existing name[" + name + "]"); } } else { throw new NullPointerException("Trying to register a null UI Component!"); } } /** * Unregisters a uiComp from the application. * @param name the name of the UI component to be unregistered */ public static void unregister(final String name) { if (name != null) { if (instance.components.get(name) != null) { instance.components.remove(name); } else { throw new RuntimeException("Unregistering a uiComp that has been registered [" + name + "]"); } } else { throw new NullPointerException("Trying to unregister with a null name!"); } } /** * Displays a message in the status bar (Note: this updates on the SwingThread via SwingUtilities) * @param text the text to be displayed */ public static void displayStatusBarText(final String text) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (instance.statusBar != null) { instance.statusBar.setText(text == null ? "" : text); } } }); } /** * Displays a message in the status bar (Note: this updates on the SwingThread via SwingUtilities) * @param text the text to be displayed */ public static void displayStatusBarErrMsg(final String text) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (instance.statusBar != null && text != null) { instance.statusBar.setErrorMessage(text); } } }); } /** * Displays a message in the status bar. (Note: this updates on the SwingThread via SwingUtilities) * @param key the key of the string that is to appear in the status bar. The resource string will be looked up * @param args for the message */ public static void displayLocalizedStatusBarText(final String key, final Object... args) { if (key == null) throw new NullPointerException("Call to displayLocalizedStatusBarText cannot be null!"); String localizedStr = instance.getResourceStringInternal(key); assert localizedStr != null : "Localized String for key[" + key + "]"; displayStatusBarText(String.format(localizedStr, args)); } /** * Displays an error message in the status bar. (Note: this updates on the SwingThread via SwingUtilities) * @param key the key of the string that is to appear in the status bar. The resource string will be looked up * @param args for the message */ public static void displayLocalizedStatusBarError(final String key, final Object... args) { if (key == null) throw new NullPointerException("Call to displayLocalizedStatusBarText cannot be null!"); String localizedStr = instance.getResourceStringInternal(key); assert localizedStr != null : "Localized String for key[" + key + "]"; displayStatusBarErrMsg(String.format(localizedStr, args)); } /** * Repaints the top most frame. * */ public static void forceTopFrameRepaint() { SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame = ((JFrame) instance.topWindow); assert frame != null : "The top frame has not been registered"; frame.repaint(); } }); } /** * Returns the glass pane from the mgr. * @return Returns the glass pane from the mgr */ public static GhostGlassPane getGlassPane() { return ((GhostGlassPane) instance.components.get(GLASSPANE)); } /** * Display an Error dialog. * @param msg the message to be displayed */ public static void displayErrorDlg(final String msg) { JOptionPane.showMessageDialog(getMostRecentWindow(), msg, getResourceString("Error"), JOptionPane.ERROR_MESSAGE); } /** * Display an Error dialog that gets its string from the resource bundle. * @param msg the message to be displayed */ public static void displayErrorDlgLocalized(final String key, Object... args) { JOptionPane.showMessageDialog(getMostRecentWindow(), String.format(getResourceString(key), args), getResourceString("Error"), JOptionPane.ERROR_MESSAGE); } /** * Display an Information dialog that gets its string from the resource bundle. * @param key the message to be displayed * @param args for formatting msg */ public static void displayInfoMsgDlgLocalized(final String key, Object... args) { JOptionPane.showMessageDialog(getMostRecentWindow(), String.format(getResourceString(key), args), getResourceString("INFORMATION"), JOptionPane.INFORMATION_MESSAGE); } /** * Display an Information dialog that gets its string from the resource bundle. * @param msg the already localized message to be displayed */ public static void displayInfoMsgDlg(final String msg) { JOptionPane.showMessageDialog(getMostRecentWindow(), msg, getResourceString("INFORMATION"), JOptionPane.INFORMATION_MESSAGE); } /** * Display an Confirmation Dialog where everything comes from the bundle. * @param titleKey the key to the dialog title * @param msgKey the key to the dialog message * @param keyBtn1 the key to the first button * @param keyBtn2 the key to the second button * @param iconOption the icon to show * @return true is YES false if NO */ public static boolean displayConfirm(final String title, final String msg, final String keyBtn1, // Yes final String keyBtn2, // No final int iconOption) { // Custom button text // NOTE: clicking the 'X' returns a -1 Object[] options = { keyBtn1, keyBtn2 }; return JOptionPane.showOptionDialog(getMostRecentWindow(), msg, title, JOptionPane.YES_NO_OPTION, iconOption, null, options, options[1]) == JOptionPane.YES_OPTION; } /** * Display an Confirmation Dialog where everything comes from the bundle. * @param titleKey the key to the dialog title * @param msgKey the key to the dialog message * @param keyBtn1 the key to the first button * @param keyBtn2 the key to the second button * @param iconOption the icon to show * @return true is YES false if NO */ public static boolean displayConfirmLocalized(final String titleKey, final String msgKey, final String keyBtn1, final String keyBtn2, final int iconOption) { return displayConfirm(getResourceString(titleKey), getResourceString(msgKey), getResourceString(keyBtn1), getResourceString(keyBtn2), iconOption); } /** * Display an Confirmation Dialog where everything comes from the bundle. * @param titleKey the key to the dialog title * @param msgKey the key to the dialog message * @param keyBtn1 the key to the first button * @param keyBtn2 the key to the second button * @param keyBtn3 the key to the third button * @param iconOption the icon to show * @return YES_OPTION, NO_OPTION, CANCEL_OPTION */ public static int displayConfirm(final String title, final String msg, final String keyBtn1, // Yes final String keyBtn2, // No final String keyBtn3, // Cancel final int iconOption) { // Custom button text Object[] options = { keyBtn1, keyBtn2, keyBtn3 }; return JOptionPane.showOptionDialog(getMostRecentWindow(), msg, title, JOptionPane.YES_NO_CANCEL_OPTION, iconOption, null, options, options[2]); } /** * Display an Confirmation Dialog where everything comes from the bundle. * @param titleKey the key to the dialog title * @param msgKey the key to the dialog message * @param keyBtn1 the key to the first button * @param keyBtn2 the key to the second button * @param keyBtn3 the key to the third button * @param iconOption the icon to show * @return YES_OPTION, NO_OPTION, CANCEL_OPTION */ public static int displayConfirmLocalized(final String titleKey, final String msgKey, final String keyBtn1, final String keyBtn2, final String keyBtn3, final int iconOption) { return displayConfirm(getResourceString(titleKey), getResourceString(msgKey), getResourceString(keyBtn1), getResourceString(keyBtn2), getResourceString(keyBtn3), iconOption); } //---------------------------------------------------------------------------------- // File Cache Section //---------------------------------------------------------------------------------- /** * Returns the longTermCache. * @return the longTermCache. */ public static FileCache getLongTermFileCache() { synchronized (instance) { if (instance.longTermCache == null) { try { instance.longTermCache = new FileCache("longTerm.Cache"); // set the cache size to 20 MB instance.longTermCache.setMaxCacheSize(20); // 20 megabytes } catch (Exception ex) { ex.printStackTrace(); edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(UIRegistry.class, ex); log.error(ex); } } } return instance.longTermCache; } /** * Sets the longTermCache. * @param longTermCache The longTermCache to set. */ public static void setLongTermFileCache(FileCache longTermCache) { instance.longTermCache = longTermCache; } /** * Gets the shortTermCache. * @return the shortTermCache. */ public static FileCache getShortTermFileCache() { synchronized (instance) { if (instance.shortTermCache == null) { try { instance.shortTermCache = new FileCache(); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(UIRegistry.class, ex); ex.printStackTrace(); log.error(ex); } } } return instance.shortTermCache; } /** * Sets the shortTermCache. * @param shortTermCache The shortTermCache to set. */ public static void setShortTermFileCache(FileCache shortTermCache) { instance.shortTermCache = shortTermCache; } /** * Returns the forms cache. * @return the forms cache. */ public static FileCache getFormsCache() { synchronized (instance) { if (instance.formsCache == null) { try { instance.formsCache = new FileCache("forms.Cache"); // turn off enforcement of the max cache size instance.formsCache.setEnforceMaxCacheSize(false); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(UIRegistry.class, ex); ex.printStackTrace(); log.error(ex); } } } return instance.formsCache; } /** * Sets the forms cache. * @param formsCache The forms cache to set. */ public static void setFormsCache(FileCache formsCache) { instance.formsCache = formsCache; } /** * Creates the initial font mapping from the base font size to the other sizes. * @param clazz the class of the component * @param baseFontArg the base font size */ protected static void adjustAllFonts(final Font oldBaseFont, final Font baseFontArg) { if (oldBaseFont != null && baseFontArg != null) { int fontSize = baseFontArg.getSize(); int oldFontSize = oldBaseFont.getSize(); String family = baseFontArg.getFamily(); UIDefaults uiDefaults = UIManager.getDefaults(); Enumeration<Object> e = uiDefaults.keys(); while (e.hasMoreElements()) { Object key = e.nextElement(); if (key.toString().endsWith(".font")) { FontUIResource fontUIRes = (FontUIResource) uiDefaults.get(key); if (fontSize != fontUIRes.getSize() || !family.equals(fontUIRes.getFamily())) { UIManager.put(key, new FontUIResource(new Font(family, fontUIRes.getStyle(), fontSize + (fontUIRes.getSize() - oldFontSize)))); } } } } } /** * Check the font against the System base font. * @param font the new font. * @return the original System Base font if the family name and size matches. For some OSs the actual * System Base Font is different than creating it. */ public static Font adjustPerDefaultFont(final Font font) { return font.getFamily().equals(instance.defaultFont.getFamily()) && instance.defaultFont.getSize() == font.getSize() ? instance.defaultFont : font; } /** * Return the base font for the UI component. * @return the base font for the UI. */ public static Font getBaseFont() { return instance.baseFont != null ? instance.baseFont : UIHelper.createLabel("").getFont(); } /** * Sets the base font and builds all the control's fonts. * @param newBaseFont the new font */ public static void setBaseFont(final Font newBaseFont) { if (instance.baseFont != newBaseFont && instance.baseFont != null) { adjustAllFonts(instance.baseFont, newBaseFont); } instance.baseFont = newBaseFont; } /** * @param defaultFont the default font */ public static void setDefaultFont(Font defaultFont) { instance.defaultFont = defaultFont; } /** * @return the default font */ public static Font getDefaultFont() { return instance.defaultFont; } //--------------------------------------------------------------------------------- //-- Glass Pane Buffered Image //--------------------------------------------------------------------------------- protected static SoftReference<BufferedImage> glassPaneBufferedImageSR; /** * Reads in the disciplines file (is loaded when the class is loaded). * @return Reads in the disciplines file (is loaded when the class is loaded). */ public static BufferedImage getGlassPaneBufferedImage(final int width, final int height) { BufferedImage bufImg = null; if (glassPaneBufferedImageSR != null) { bufImg = glassPaneBufferedImageSR.get(); } if (bufImg != null) { if (bufImg.getWidth() != width && bufImg.getHeight() != height) { bufImg = null; } } if (bufImg == null) { glassPaneBufferedImageSR = new SoftReference<BufferedImage>( new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)); } return glassPaneBufferedImageSR.get(); } /** * Writes a string message into the BufferedImage on GlassPane and sets the main component's visibility to false and * shows the GlassPane. * @param msg the message * @param pointSize the Font point size for the message to be writen in */ public static GhostGlassPane writeGlassPaneMsg(final String msg, final int pointSize) { GhostGlassPane glassPane = getGlassPane(); if (glassPane != null) { glassPane.finishDnD(); } glassPane.setMaskingEvents(true); Component mainComp = get(MAINPANE); if (mainComp != null && glassPane != null) { JFrame frame = (JFrame) get(FRAME); frameRect = frame.getBounds(); int y = 0; JMenuBar menuBar = null; Dimension size = mainComp.getSize(); if (UIHelper.getOSType() != UIHelper.OSTYPE.MacOSX) { menuBar = frame.getJMenuBar(); size.height += menuBar.getSize().height; y += menuBar.getSize().height; } BufferedImage buffer = getGlassPaneBufferedImage(size.width, size.height); Graphics2D g2 = buffer.createGraphics(); if (menuBar != null) { menuBar.paint(g2); } g2.translate(0, y); mainComp.paint(g2); g2.translate(0, -y); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(255, 255, 255, 128)); g2.fillRect(0, 0, size.width, size.height); g2.setFont(new Font((new JLabel()).getFont().getName(), Font.BOLD, pointSize)); FontMetrics fm = g2.getFontMetrics(); int tw = fm.stringWidth(msg); int th = fm.getHeight(); int tx = (size.width - tw) / 2; int ty = (size.height - th) / 2; int expand = 20; int arc = expand * 2; g2.setColor(Color.WHITE); g2.fillRoundRect(tx - (expand / 2), ty - fm.getAscent() - (expand / 2), tw + expand, th + expand, arc, arc); g2.setColor(Color.DARK_GRAY); g2.drawRoundRect(tx - (expand / 2), ty - fm.getAscent() - (expand / 2), tw + expand, th + expand, arc, arc); g2.setColor(Color.BLACK); g2.drawString(msg, tx, ty); g2.dispose(); glassPane.setImage(buffer); glassPane.setPoint(new Point(0, 0), GhostGlassPane.ImagePaintMode.ABSOLUTE); glassPane.setOffset(new Point(0, 0)); glassPane.setVisible(true); mainComp.setVisible(false); //Using paintImmediately fixes problems with glass pane not showing, such as for workbench saves initialed //during workbench or app shutdown. Don't know if there is a better way to fix it. //glassPane.repaint(); glassPane.paintImmediately(glassPane.getBounds()); showingGlassPane = true; } return glassPane; } /** * Clears the GlassPane, meaning it sets the mainComp visible again and sets the GlassPane to be hidden. */ public synchronized static void clearGlassPaneMsg() { Component mainComp = UIRegistry.get(UIRegistry.MAINPANE); if (mainComp != null && getGlassPane() != null && getGlassPane().isVisible()) { getGlassPane().setMaskingEvents(false); getGlassPane().setVisible(false); mainComp.setVisible(true); mainComp.repaint(); Frame frame = (JFrame) get(FRAME); frame.setBounds(frameRect); } showingGlassPane = false; } /** * @return */ public static boolean isShowingGlassPane() { return showingGlassPane; } /** * @param msg * @param pointSize */ public static SimpleGlassPane writeSimpleGlassPaneMsg(final String msg, final int pointSize) { return writeSimpleGlassPaneMsg(msg, pointSize, false); } /** * @param msg * @param pointSize * @param isDblClickProgBar * @return */ public static SimpleGlassPane writeSimpleGlassPaneMsg(final String msg, final int pointSize, final boolean isDblClickProgBar) { SimpleGlassPane glassPane = new SimpleGlassPane(msg, pointSize, false, isDblClickProgBar); JStatusBar statusBar = UIRegistry.getStatusBar(); if (statusBar != null) { glassPane.setMargin(new Insets(0, 0, statusBar.getSize().height, 0)); } oldGlassPane = UIRegistry.getGlassPane(); if (oldGlassPane != null) { oldGlassPane.finishedWithDragAndDrop(); } if (glassPane != null && UIRegistry.getTopWindow() != null) { ((JFrame) UIRegistry.getTopWindow()).setGlassPane(glassPane); glassPane.setVisible(true); showingGlassPane = true; } else { oldGlassPane = null; showingGlassPane = false; } return glassPane; } /** * Fades screen and writes message to screen * @param localizedMsg the already localized message */ public static void writeTimedSimpleGlassPaneMsg(final String localizedMsg) { writeTimedSimpleGlassPaneMsg(localizedMsg, null, null, null, false); } /** * Fades screen and writes message to screen * @param localizedMsg the already localized message * @param textColor the color of the text */ public static void writeTimedSimpleGlassPaneMsg(final String localizedMsg, final Color textColor) { writeTimedSimpleGlassPaneMsg(localizedMsg, null, textColor, null, false); } /** * Fades screen and writes message to screen * @param localizedMsg the already localized message * @param milliseconds the number of milliseconds to pause showing the message * @param doHideOnClick true to hide message on user click */ public static void writeTimedSimpleGlassPaneMsg(final String localizedMsg, final Integer milliseconds, final boolean doHideOnClick) { writeTimedSimpleGlassPaneMsg(localizedMsg, milliseconds, null, null, doHideOnClick); } /** * Fades screen and writes message to screen * @param localizedMsg the already localized message * @param milliseconds the number of milliseconds to pause showing the message * @param textColor the color of the text * @param pointSize the point size to draw the text * @param doHideOnClick true to hide message on user click */ public static void writeTimedSimpleGlassPaneMsg(final String localizedMsg, final Integer milliseconds, final Color textColor, final Integer pointSize, final boolean doHideOnClick) { writeTimedSimpleGlassPaneMsg(localizedMsg, milliseconds, textColor, pointSize, doHideOnClick, null); } /** * Fades screen and writes message to screen * @param localizedMsg the already localized message * @param milliseconds the number of milliseconds to pause showing the message * @param textColor the color of the text * @param pointSize the point size to draw the text * @param yTextPos set the the 'y' position of the text */ public static void writeTimedSimpleGlassPaneMsg(final String localizedMsg, final Integer milliseconds, final Color textColor, final Integer pointSize, final boolean doHideOnClick, final Integer yTextPos) { final SimpleGlassPane sgp = UIRegistry.writeSimpleGlassPaneMsg(localizedMsg, pointSize == null ? STD_FONT_SIZE : pointSize); if (sgp != null) { sgp.setTextColor(textColor); sgp.setHideOnClick(doHideOnClick); sgp.setTextYPos(yTextPos); } SwingWorker<Integer, Integer> msgWorker = new SwingWorker<Integer, Integer>() { /* (non-Javadoc) * @see javax.swing.SwingWorker#doInBackground() */ @Override protected Integer doInBackground() throws Exception { try { Thread.sleep(milliseconds != null ? milliseconds : STD_WAIT_TIME); } catch (Exception ex) { ex.printStackTrace(); } return null; } @Override protected void done() { super.done(); if (sgp != null) { sgp.setTextColor(null); } UIRegistry.clearSimpleGlassPaneMsg(); if (textColor == Color.RED) { getStatusBar().setErrorMessage(localizedMsg); } else { displayStatusBarText(localizedMsg); } } }; msgWorker.execute(); } /** * */ public synchronized static void clearSimpleGlassPaneMsg() { if (oldGlassPane != null) { ((JFrame) UIRegistry.getTopWindow()).setGlassPane(oldGlassPane); oldGlassPane.setVisible(false); } else { Component glassPane = ((JFrame) UIRegistry.getTopWindow()).getGlassPane(); if (glassPane != null) { glassPane.setVisible(false); } } oldGlassPane = null; showingGlassPane = false; } /** * @param isMobile */ public static void setMobile(final boolean isMobile) { UIRegistry.isMobile = isMobile; } /** * @return */ public static boolean isMobile() { return isMobile != null && isMobile; } /** * @return the isEmbedded */ public static Boolean isEmbedded() { return isEmbedded != null && isEmbedded; } /** * @param isEmbedded the isEmbedded to set */ public static void setEmbedded(final Boolean isEmbedded) { UIRegistry.isEmbedded = isEmbedded; } /** * Sets the path to the embedded DB. * @param path the path. */ public static void setEmbeddedDBPath(final String path) { dumpCanonicalPath("setEmbeddedDBDir", path); if (StringUtils.isNotEmpty(path)) { System.setProperty(EMBEDDED_DB_PATH, path); } } /** * @return the path to the embedded DB */ public static String getEmbeddedDBPath() { return System.getProperty(EMBEDDED_DB_PATH); } /** * @param desc * @param path */ private static void dumpCanonicalPath(final String desc, final String path) { dumpCanonicalPath(desc, new File(path)); } /** * @param desc * @param path */ private static void dumpCanonicalPath(final String desc, final File path) { if (debugPaths) { try { log.debug("***** dumpCanonicalPath: " + desc + ": [" + path.getCanonicalPath() + "]"); } catch (Exception ex) { } } } /** * @return a default path to the embedded DB when it is suppose to be on the local machine. */ public static String getDefaultEmbeddedDBPath() { dumpCanonicalPath("getDefaultEmbeddedDBPath", getAppDataDir() + File.separator + EMBEDDED_DB_DIR); return UIRegistry.getAppDataDir() + File.separator + EMBEDDED_DB_DIR; } /** * @return a default path to the embedded DB when it is suppose to be on removable media. * Sos it is placed relative to the executable. */ public static String getDefaultMobileEmbeddedDBPath() { dumpCanonicalPath("getDefaultMobileEmbeddedDBPath", UIRegistry.getDefaultWorkingPath() + File.separator + EMBEDDED_DB_DIR); return UIRegistry.getDefaultWorkingPath() + File.separator + EMBEDDED_DB_DIR; } /** * @return a default path to the embedded DB when it is suppose to be on removable media. * Sos it is placed relative to the executable. */ public static String getDefaultMobileEmbeddedDBPath(final String dbName) { dumpCanonicalPath("getDefaultMobileEmbeddedDBPath", UIRegistry.getDefaultWorkingPath() + File.separator + dbName); String path = UIRegistry.getDefaultWorkingPath(); String mobileRelativePath = System.getProperty("mobilesrcdir"); if (StringUtils.isNotEmpty(mobileRelativePath)) { if (!path.endsWith(File.separator) && !mobileRelativePath.startsWith(File.separator)) { path += File.separator; } path += mobileRelativePath; } return path + File.separator + dbName; } /** * Sets the path to the embedded DB. * @param path the path. */ public static void setMobileEmbeddedDBPath(final String path) { dumpCanonicalPath("setMobileEmbeddedDBPath", path); if (StringUtils.isNotEmpty(path)) { System.setProperty(MOBILE_EMBEDDED_DB_PATH, path); } } /** * @return the path to the embedded DB */ public static String getMobileEmbeddedDBPath() { return System.getProperty(MOBILE_EMBEDDED_DB_PATH); } //--------------------------------------------------------- //-- Undo / Redo and Other Action Stuff //--------------------------------------------------------- /** * Returns an Action by name. * @param name the name of the action */ public static Action getAction(final String name) { return instance.actionMap.get(name); } /** * Register's an action. * @param name thee name of the action * @param action the action * @return the action passed in */ public static Action registerAction(final String name, final Action action) { if (instance.actionMap.get(name) == null) { instance.actionMap.put(name, action); } else { log.error("Action with name[" + name + "] is already registered."); } return action; } /** * Register's an action. * @param name thee name of the action * @param action the action * @return the action passed in */ public static void unregisterAction(final String name) { if (instance.actionMap.get(name) != null) { instance.actionMap.remove(name); } else { log.error("Couldn't find Action with name[" + name + "]."); } } /** * Adds Key navigation bindings to a component. (Is this needed?) * @param comp the component */ public void addNavBindings(JComponent comp) { InputMap inputMap = comp.getInputMap(); //Ctrl-b to go backward one character KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_B, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); inputMap.put(key, DefaultEditorKit.backwardAction); //Ctrl-f to go forward one character key = KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); inputMap.put(key, DefaultEditorKit.forwardAction); //Ctrl-p to go up one line key = KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); inputMap.put(key, DefaultEditorKit.upAction); //Ctrl-n to go down one line key = KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); inputMap.put(key, DefaultEditorKit.downAction); } // Create the edit menu public JMenu createEditMenu() { JMenu menu = new JMenu(getResourceString("EDIT")); menu.setMnemonic(KeyEvent.VK_E); // Undo and redo are actions of our own creation. undoAction = (UndoAction) makeAction(UndoAction.class, this, "Undo", null, null, new Integer(KeyEvent.VK_Z), KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); register(UNDO, menu.add(undoAction)); actionMap.put(UNDO, undoAction); redoAction = (RedoAction) makeAction(RedoAction.class, this, "Redo", null, null, new Integer(KeyEvent.VK_Y), KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); register(REDO, menu.add(redoAction)); actionMap.put(REDO, redoAction); menu.addSeparator(); // These actions come from the default editor kit. Get the ones we want // and stick them in the menu. Action cutAction = makeAction(DefaultEditorKit.CutAction.class, null, "Cut", null, "Cut selection to clipboard", // I18N ???? new Integer(KeyEvent.VK_X), KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); register(CUT, menu.add(cutAction)); cutAction.setEnabled(false); actionMap.put(CUT, cutAction); Action copyAction = makeAction(DefaultEditorKit.CopyAction.class, null, "Copy", null, "Copy selection to clipboard", new Integer(KeyEvent.VK_C), KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); register(COPY, menu.add(copyAction)); copyAction.setEnabled(false); actionMap.put(COPY, copyAction); Action pasteAction = makeAction(DefaultEditorKit.PasteAction.class, null, "Paste", null, "Paste contents of clipboard", new Integer(KeyEvent.VK_V), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); pasteAction.setEnabled(false); register(PASTE, menu.add(pasteAction)); actionMap.put(PASTE, pasteAction); /* menu.addSeparator(); Action selectAllAction = makeAction(SelectAllAction.class, this, "Select All", null, "Select all text", new Integer(KeyEvent.VK_A), KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menu.add(selectAllAction); */ launchFindReplaceAction = (LaunchFindReplaceAction) makeAction(LaunchFindReplaceAction.class, this, "Find", null, null, new Integer(KeyEvent.VK_F), KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); //menu.add(launchFindReplaceAction); // launchFindReplaceAction.setEnabled(false); // register(FIND, menu.add(launchFindReplaceAction)); // actionMap.put(FIND, launchFindReplaceAction); launchFindReplaceAction.setEnabled(false); register(FIND, menu.add(launchFindReplaceAction)); actionMap.put(FIND, launchFindReplaceAction); return menu; } /** * Enables/Disables Cut/Copy/Paste. * @param enable true/false */ public static void enableCutCopyPaste(final boolean enable) { instance.actionMap.get(CUT).setEnabled(enable); instance.actionMap.get(COPY).setEnabled(enable); instance.actionMap.get(PASTE).setEnabled(enable); } /** * @param actionClass * @param owner * @param name * @param icon * @param toolTip * @param mnemonicKeyCode * @param acceleratorKey * @return */ public Action makeAction(Class<?> actionClass, Object owner, String name, ImageIcon icon, String toolTip, Integer mnemonicKeyCode, KeyStroke acceleratorKey) { Action a = null; try { Constructor<?> c; if (owner != null) { // If the class is an inner class, its constuctor takes a hidden // parameter, an object of it enclosing class. c = actionClass.getConstructor(new Class<?>[] { owner.getClass() }); a = (Action) c.newInstance(new Object[] { owner }); } else { c = actionClass.getConstructor((Class<?>[]) null); a = (Action) c.newInstance((Object[]) null); } return makeAction(a, name, icon, toolTip, mnemonicKeyCode, acceleratorKey); } catch (ClassCastException e) { System.err.println("actionClass argument " + actionClass + " does not implement Action"); } catch (Exception e) { System.err.println(e + " -- while trying to make an instance of " + actionClass); try { // Output a list of constructors available for this class. Constructor<?>[] cc = actionClass.getConstructors(); for (int i = 0; i < cc.length; i++) { System.err.println(cc[i].toString()); } } catch (Exception ee) { log.error(ee); } } return a; } /** * @param action * @param name * @param icon * @param toolTip * @param mnemonicKeyCode * @param acceleratorKey * @return */ public Action makeAction(Action action, String name, ImageIcon icon, String toolTip, Integer mnemonicKeyCode, KeyStroke acceleratorKey) { if (name != null) action.putValue(Action.NAME, name); if (icon != null) action.putValue(Action.SMALL_ICON, icon); if (toolTip != null) action.putValue(Action.SHORT_DESCRIPTION, toolTip); if (mnemonicKeyCode != null) action.putValue(Action.MNEMONIC_KEY, mnemonicKeyCode); if (acceleratorKey != null) action.putValue(Action.ACCELERATOR_KEY, acceleratorKey); return action; } // This nested class is the child of desperation. If SelectAllAction was a // public static nested class of DefaultEditorKit as CopyAction is, this // hack wouldn't be needed. /*public class SelectAllAction extends AbstractAction { private final Action realAction = getActionByName(DefaultEditorKit.selectAllAction); public SelectAllAction() { super(); } public SelectAllAction(final UIRegistry uic) { super(); } public void actionPerformed(ActionEvent e) { realAction.actionPerformed(e); } }*/ // The following two methods allow us to find an // action provided by the editor kit by its name. /*public HashMap<Object, Action> createActionTable(JTextComponent textComponent) { actions = new HashMap<Object, Action>(); Action[] actionsArray = textComponent.getActions(); for (int i = 0; i < actionsArray.length; i++) { Action a = actionsArray[i]; actions.put(a.getValue(Action.NAME), a); } return actions; }*/ /** * Returns an action name. * @param altName * @return */ /*private Action getActionByName(String name) { return actions.get(name); }*/ //------------------------------------------------------ //-- An interface for all those wanting to play nice with //-- the undo mechanism //------------------------------------------------------ public interface UndoableTextIFace { /** * @return the UndoManager */ public UndoManager getUndoManager(); /** * @return the JTextComponent that does undo */ public JTextComponent getTextComponent(); } //------------------------------------------------------ //-- The UndoAction //------------------------------------------------------ public class UndoAction extends AbstractAction { protected UndoManager undoManager = null; public UndoAction() { super("Undo"); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { if (undoManager.canUndo()) { undoManager.undo(); } } catch (CannotUndoException ex) { ex.printStackTrace(); } updateUndoState(); undoAction.updateUndoState(); } protected void updateUndoState() { if (undoManager != null && undoManager.canUndo()) { setEnabled(true); putValue(Action.NAME, undoManager.getUndoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, "Undo"); } } /** * @return the undo */ public UndoManager getUndoManager() { return undoManager; } /** * @param undo the undo to set */ public void setUndoManager(UndoManager undo) { this.undoManager = undo; setEnabled(undo != null); } } //------------------------------------------------------ //-- The RedoAction //------------------------------------------------------ public class RedoAction extends AbstractAction { protected UndoManager undoManager = null; public RedoAction() { super("Redo"); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { if (undoManager.canRedo()) { undoManager.redo(); } } catch (CannotRedoException ex) { log.error("Unable to redo: " + ex); ex.printStackTrace(); } updateRedoState(); undoAction.updateUndoState(); } protected void updateRedoState() { if (undoManager != null && undoManager.canRedo()) { setEnabled(true); putValue(Action.NAME, undoManager.getRedoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, "Redo"); } } /** * @return the undo */ public UndoManager getUndoManager() { return undoManager; } /** * @param undo * the undo to set */ public void setUndoManager(UndoManager undo) { this.undoManager = undo; setEnabled(undo != null); } } //------------------------------------------------------ //-- The LaunchFindAction Action //------------------------------------------------------ public class LaunchFindReplaceAction extends AbstractAction { protected SearchReplacePanel searchReplacePanel = null; public LaunchFindReplaceAction() { super("Find"); setEnabled(false); } @SuppressWarnings("synthetic-access") public void actionPerformed(ActionEvent e) { log.debug("Ctrl-f hit from with UIRegistry - passing action onto the SearchReplacePanel"); if (this.isEnabled()) { if (searchReplacePanel != null) { searchReplacePanel.getLaunchFindAction().actionPerformed(e); } else { log.error("search panel is null"); } } } public void removeSearchPanel() { this.searchReplacePanel = null; setEnabled(false); } /** * @return the undo */ public SearchReplacePanel getSearchReplacePanel() { return searchReplacePanel; } /** */ @SuppressWarnings("synthetic-access") public void setSearchReplacePanel(SearchReplacePanel panel) { this.searchReplacePanel = panel; if (panel == null) { setEnabled(true); } } } //------------------------------------------------------ //-- Listens for edits that can be undone. //------------------------------------------------------ public class UICUndoableEditListener implements UndoableEditListener { protected UndoManager undoManager; public UICUndoableEditListener(final UndoManager undoManager) { this.undoManager = undoManager; } /* (non-Javadoc) * @see javax.swing.event.UndoableEditListener#undoableEditHappened(javax.swing.event.UndoableEditEvent) */ public void undoableEditHappened(UndoableEditEvent e) { //Remember the edit and update the menus. /*if (undoManager != null) { undoManager.addEdit(e.getEdit()); } if (undoAction != null) { undoAction.updateUndoState(); } if (redoAction != null) { redoAction.updateRedoState(); }*/ } } /** * @return the redoAction */ public static RedoAction getRedoAction() { return redoAction; } /** * @return the undoAction */ public static UndoAction getUndoAction() { return undoAction; } /** * @return the launchFindReplaceAction */ public static LaunchFindReplaceAction getLaunchFindReplaceAction() { return launchFindReplaceAction; } public static void disableFindFromEditMenu() { getLaunchFindReplaceAction().setEnabled(false); instance.actionMap.get(FIND).setEnabled(false); } public static void enableFind(final SearchReplacePanel findPanel, final boolean enable) { getLaunchFindReplaceAction().setSearchReplacePanel(findPanel); instance.actionMap.get(FIND).setEnabled(enable); } /** * @param undoableText */ public void hookUpUndoableEditListener(final UndoableTextIFace undoableText) { undoableText.getTextComponent().getDocument() .addUndoableEditListener(new UICUndoableEditListener(undoableText.getUndoManager())); } /** * @param nameStr * @param enable * @param selected */ public static void enableActionAndMenu(final String nameStr, final boolean enable, final Boolean selected) { Action action = UIRegistry.getAction(nameStr); if (action != null) { action.setEnabled(enable); } Component comp = UIRegistry.get(nameStr); if (comp instanceof JCheckBoxMenuItem) { ((JCheckBoxMenuItem) comp).setEnabled(enable); if (selected != null) { ((JCheckBoxMenuItem) comp).setSelected(selected); } } else if (comp instanceof JMenuItem) { ((JMenuItem) comp).setEnabled(enable); } } //----------------------------------------------------------------- //-- Inner Classes //----------------------------------------------------------------- class ResBundleInfo { protected String name; protected ResourceBundle resBundle; public ResBundleInfo(String name, ResourceBundle resBundle) { super(); this.name = name; this.resBundle = resBundle; } /** * @return the name */ public String getName() { return name; } /** * @return the resBundle */ public ResourceBundle getResBundle() { return resBundle; } } }