/*
* Licensed Materials - Property of Perforce Software, Inc.
* © Copyright Perforce Software, Inc. 2014, 2021
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package shared;
import ilog.views.gantt.action.IlvAction;
import ilog.views.util.IlvImageUtil;
import ilog.views.util.java.Version;
import ilog.views.util.swing.IlvSwingUtil;
import ilog.views.util.swing.color.IlvJColorChooser;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.MenuDragMouseEvent;
import javax.swing.event.MenuDragMouseListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolTipUI;
import javax.swing.text.View;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import shared.help.HelpSystem;
import shared.help.NoHelpSystem;
/**
* This is the abstract base class for all of the Gantt module demos. It
* contains much of the common code for handling the menus, the status bar,
* and so on.
*/
public abstract class AbstractExample extends JRootPane {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExample.class);
// =========================================
// Instance Variables
// =========================================
/**
* The adapter that collects the status text of each menu and displays it in
* the status bar.
*/
private MenuInfo menuStatusAdapter;
/**
* The status bar used for showing information when menu items are selected.
*/
private JLabel status;
/**
* The toolbar.
*/
private JToolBar toolbar;
/**
* The example's top-level ancestor. This is the first ancestor of the example
* that is a Window.
*/
private JFrame rootAncestor;
/**
* The example's explicit locale. This is a duplication of the private
* <code>locale</code> field of the <code>Component</code> superclass. It is
* repeated here so that we can test whether the locale has been explicity
* set or is being inherited from its parent container.
*/
private Locale locale = null;
/**
* A blank icon.
*/
private Icon blankIcon = null;
/**
* Indicates whether we have attempted to load the blank icon.
*/
private boolean blankIconInit = false;
/**
* Indicates whether this example has permission to access the
* local file system.
*/
private Boolean localFileAccessAllowed;
/**
* The help system for the example.
*/
private HelpSystem helpSystem;
/**
* Help actions.
*/
protected IlvAction helpAction;
protected IlvAction contextHelpAction;
// =========================================
// Class Constants
// =========================================
/**
* The client-property lookup key for status text of each menu item.
*/
private static final String STATUS_TEXT_KEY = "StatusText";
/**
* The client-property lookup key for whether a <code>JMenu</code> has any
* menu item icons.
*/
private static final String MENU_ITEM_ICONS_KEY = "MenuItemIcons";
/**
* The blank icon filename.
*/
private static final String BLANK_ICON = "images/blank.gif";
// =========================================
// Instance Construction and Initialization
// =========================================
/**
* Creates a new <code>AbstractExample</code>.
*/
public AbstractExample() {
// This will prevent the "SystemEventQueue" access message from appearing
// in the Java console when this example is in a browser.
getRootPane().putClientProperty("defeatSystemEventQueueCheck",
Boolean.TRUE);
menuStatusAdapter = new MenuInfo();
}
/**
* Initializes the example's user interface in the specified container.
*
* @param container The container that the example is running in. This will
* be the <code>JFrame</code> of the application.
*/
public void init(Container container) {
// If the locale has not been explicitly set, use the locale of the
// container.
if (locale == null) {
setLocale(container.getLocale());
}
rootAncestor=(JFrame) SwingUtilities.getWindowAncestor(container);
// Load custom L&Fs using the app's classloader. This works around JavaSoft
// Bug #4155617, which prevents custom L&Fs from being loaded by the plugin.
UIManager.put("ClassLoader", getClass().getClassLoader());
// Disable the Metal tooltip L&F because, as of JDK 1.2.2, it displays
// accelerator bindings incorrectly. See JavaSoft Bug #4306876.
UIManager.put("ToolTipUI", "shared.AbstractExample$ToolTipUI");
// Initialize the Help System
initHelpSystem();
}
/**
* This method is invoked after the example's user interface has been displayed
* for the first time. You can override this method to perform customizations that
* require a correctly positioned and laid out user interface, such as zooming to
* fit.
*/
public void postInit() {
}
/**
* Returns the title of the example.
*
* @return The title of the example.
*/
public abstract String getTitle();
/**
* Sets the locale of the example. This method is overridden to store a
* locally accessible copy of the locale.
*
* @param locale The locale.
*/
Override
public void setLocale(Locale locale) {
this.locale = locale;
super.setLocale(locale);
}
/**
* Sets the language-sensitive orientation of the example.
* @param o The orientation.
*/
Override
public void setComponentOrientation(ComponentOrientation o) {
IlvSwingUtil.updateComponentTreeOrientation(rootAncestor, o);
}
// =========================================
// Application Permissions
// =========================================
/**
* Returns <code>true</code> if this example has permission to exit the
* Java VM.
*
* @return Whether the example has permission to exit the JVM.
*/
protected boolean isExitAllowed() {
SecurityManager security = System.getSecurityManager();
if (security == null) {
return true;
}
try {
security.checkExit(0);
} catch (SecurityException e) {
return false;
}
return true;
}
/**
* Returns <code>true</code> if this example should offer printing services.
*/
protected boolean isPrintingConfigured() {
return true;
}
/**
* Returns <code>true</code> if this example has permission to print.
*
* @return Whether the example has permission to print.
*/
protected boolean isPrintingAllowed() {
SecurityManager security = System.getSecurityManager();
if (security == null) {
return true;
}
try {
security.checkPrintJobAccess();
} catch (SecurityException e) {
return false;
}
return true;
}
/**
* Returns <code>true</code> if this example has permission to access the
* local file system. Typically, this will be <code>true</code> if the example
* is running as an application.
*
* @return Whether the example has permission to access the local file system.
*/
protected boolean isLocalFileAccessAllowed() {
if (localFileAccessAllowed == null) {
localFileAccessAllowed = Boolean.TRUE;
// If there is no security manager, then we are probably running as an
// application and everything is allowed. Otherwise, we check that we can
// read the user's current directory.
SecurityManager security = System.getSecurityManager();
if (security != null) {
try {
security.checkPropertyAccess("user.dir");
String dir = System.getProperty("user.dir");
security.checkRead(dir);
} catch (SecurityException e) {
localFileAccessAllowed = Boolean.FALSE;
}
}
}
return localFileAccessAllowed;
}
// =========================================
// Accessing Resources
// =========================================
/**
* Returns the fully qualified URL of a resource file that is specified relative to the
* working directory of the example. Note that this is different than calling
* <code>Class.getResource()</code>, which performs resource lookup relative to the
* example's classpath.
*
* @param relativePath The path of the resource file, relative to the working directory
* of the example.
* @return The resource URL.
* @throws IOException if the path cannot be resolved to a valid and existing URL.
*
*/
public URL getResourceURL(String relativePath)
throws IOException {
relativePath = relativePath.replace('\\', '/');
URL url = null;
// In an application context, we try to find an external file relative to the
// current working directory. If an external file does not exist, then the file
// may be bundled into the jar with the classes. In this case, we prepend a '/'
// to relativePath so that we search relative to the classloader's root and not
// relative to the packaging of the current class.
File file = new File(relativePath);
// If the file exists in the external file system, we return its corresponding URL.
if (file.exists()) {
url = file.toURI().toURL();
}
// Otherwise, we search for the file relative to the classloader's root. This will
// find the file if it is packaged into a jar with the classes.
else {
// Prepend a '/' so that we search relative to the classloader's root and not
// relative to the packaging of the current class.
if (relativePath.charAt(0) != '/') {
relativePath = '/' + relativePath;
}
url = getClass().getResource(relativePath);
}
// Verify that we have a valid URL by trying to open its associated stream.
if (url == null) {
throw new FileNotFoundException(relativePath);
}
InputStream stream = url.openStream();
stream.close();
return url;
}
// =========================================
// Status Bar
// =========================================
/**
* Sets the status text of the specified component.
*
* @param component The component.
* @param text The status text.
*/
protected void setStatusText(JComponent component, String text) {
component.putClientProperty(STATUS_TEXT_KEY, text);
if (component instanceof JMenuItem) {
JMenuItem menuItem = (JMenuItem) component;
menuItem.addMenuDragMouseListener(menuStatusAdapter);
if (!(menuItem instanceof JMenu)) {
menuItem.addMouseMotionListener(menuStatusAdapter);
}
}
if (component instanceof JMenu) {
((JMenu) component).addMenuListener(menuStatusAdapter);
}
}
/**
* Returns the status text of the specified component.
*
* @param component The component.
* @return The status text.
*/
protected String getStatusText(JComponent component) {
return (String) component.getClientProperty(STATUS_TEXT_KEY);
}
/**
* Creates the status bar component.
*
* @return The status bar.
*/
protected JLabel createStatusBar() {
status = new JLabel(" ");
status.setBorder(new BevelBorder(BevelBorder.LOWERED));
return status;
}
/**
* Returns the status bar component.
*
* @return The status bar.
*/
protected JLabel getStatusBar() {
return status;
}
// =========================================
// ToolBar
// =========================================
/**
* Creates the toolbar component.
*
* @return The toolbar.
*/
protected JToolBar createToolBar() {
toolbar = new JToolBar();
toolbar.setFloatable(false);
populateToolBar(toolbar);
return toolbar;
}
/**
* Populates the specified toolbar. This default implementation does nothing.
* Subclasses should override this method as needed.
*
* @param toolbar The toolbar.
*/
protected void populateToolBar(JToolBar toolbar) {
}
/**
* Returns the toolbar component.
*
* @return The toolbar.
*/
protected JToolBar getToolBar() {
return toolbar;
}
/**
* Adds the specified action to a toolbar and returns the created
* <code>JButton</code>.
*
* @param toolbar The toolbar.
* @param action The action.
* @return The button.
*/
protected JButton addAction(JToolBar toolbar, IlvAction action) {
// Create the button from the action. This configures the button text, icon,
// tooltip, and mnemonic.
JButton button = new JButton(action);
// Only use text on buttons if there is no icon.
if (button.getIcon() != null) {
button.setText(null);
}
// Register the accelerator as a window-wide shortcut.
KeyStroke accelerator = action.getAccelerator();
if (accelerator != null) {
button.registerKeyboardAction(action, accelerator,
JButton.WHEN_IN_FOCUSED_WINDOW);
}
// Add the accerator key to the end of the tooltip.
String shortDescription = action.getShortDescription();
if (shortDescription != null) {
if (accelerator != null) {
shortDescription += ToolTipUI.TIP_DELIMITER
+ action.getAcceleratorText();
}
button.setToolTipText(shortDescription);
}
// Add the button to the toolbar.
toolbar.add(button);
return button;
}
// =========================================
// Menus
// =========================================
/**
* Sets the menu bar for the top-level ancestor of the sample (either the
* containing <code>JFrame</code> ).
*/
Override
public void setJMenuBar(JMenuBar menuBar) {
((JFrame)rootAncestor).setJMenuBar(menuBar);
}
/**
* Returns the blank icon that can be used to left-align menu items that do
* not have an icon of their own.
*
* @return The blank filler icon.
*/
protected Icon getBlankIcon() {
if (!blankIconInit) {
try {
Image image = IlvImageUtil.getImageFromFile(AbstractExample.class,
BLANK_ICON);
blankIcon = new ImageIcon(image);
} catch (Exception e) {
System.err.println("Warning: error while reading image " + BLANK_ICON);
}
blankIconInit = true;
}
return blankIcon;
}
/**
* Calculates whether the specified menu has any menu items with icons.
*
* @param menu The menu.
* @return Whether the menu has any items with icons.
*/
private boolean computeHasIcons(JMenu menu) {
for (int i = 0; i < menu.getMenuComponentCount(); i++) {
Component c = menu.getMenuComponent(i);
if (c instanceof AbstractButton && ((AbstractButton) c).getIcon() != null) {
return true;
}
}
return false;
}
/**
* Sets whether the specified menu has any menu items with icons.
*
* @param menu The menu.
* @param flag Indicates whether the menu has any items with icons.
*/
protected void setHasIcons(JMenu menu, boolean flag) {
menu.putClientProperty(MENU_ITEM_ICONS_KEY, flag);
}
/**
* Returns whether the specified menu has any menu items with icons.
*
* @param menu The menu.
* @return Whether the menu has any items with icons.
*/
protected boolean hasIcons(JMenu menu) {
Boolean flag = (Boolean) menu.getClientProperty(MENU_ITEM_ICONS_KEY);
if (flag == null) {
flag = computeHasIcons(menu);
menu.putClientProperty(MENU_ITEM_ICONS_KEY, flag);
}
return flag;
}
/**
* Adds the specified action to a menu and returns the created
* <code>JMenuItem</code>.
*
* @param menu The menu.
* @param action The action.
* @return The menu item.
*/
protected JMenuItem addAction(JMenu menu, IlvAction action) {
return addAction(menu, action, null);
}
/**
* Adds the specified action to a menu and makes it possible to invoke the
* action through additional accelerators. (The primary accelerator is
* <code>action.getAccelerator()</code>.)
* Returns the created <code>JMenuItem</code>.
*
* @param menu The menu.
* @param action The action.
* @param additionalAccelerators a set of keystrokes, or <code>null</code>.
* @return The menu item.
*/
protected JMenuItem addAction(JMenu menu, IlvAction action,
KeyStroke[] additionalAccelerators) {
// Create the menu item from the action. This configures the item text,
// icon, and mnemonic. Under JDK 1.4 and later, it also configures the item
// tooltip and accelerator keystroke.
JMenuItem menuItem = new JMenuItem(action);
// Under JDK 1.3 and earlier we must explicitly configure the menu item's
// accelerator from the action.
KeyStroke accelerator = action.getAccelerator();
if (accelerator != null) {
menuItem.setAccelerator(accelerator);
}
// Enable the additional accelerators.
if (additionalAccelerators != null && additionalAccelerators.length > 0) {
String name = action.getName();
// Here we assume that different actions have different names.
for (KeyStroke ks : additionalAccelerators) {
menu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, name);
}
menu.getActionMap().put(name, action);
}
// Under JDK 1.4 and later, explicitly disable the menu item tooltip.
menuItem.setToolTipText(null);
// If we are about to add the first item with an icon, set all other items
// to have a blank icon for proper alignment.
if (!hasIcons(menu) && menuItem.getIcon() != null) {
for (int i = 0; i < menu.getMenuComponentCount(); i++) {
Component c = menu.getMenuComponent(i);
if (c instanceof AbstractButton && ((AbstractButton) c).getIcon() != null) {
((AbstractButton) c).setIcon(getBlankIcon());
}
}
setHasIcons(menu, true);
}
// If we are about to add an action with no icon to a menu that has icons,
// use a blank icon for the menu item so that it is properly aligned.
if (hasIcons(menu) && menuItem.getIcon() == null) {
menuItem.setIcon(getBlankIcon());
}
// Configure the status bar text for the menu item.
String longDescription = action.getLongDescription();
if (longDescription == null) {
longDescription = " ";
}
setStatusText(menuItem, longDescription);
// Add the item to the menu.
menu.add(menuItem);
return menuItem;
}
// =========================================
// Look and Feel
// =========================================
/**
* Sets the look-and-feel of the example.
*
* @param laf The look-and-feel.
*/
protected void setLookAndFeel(UIManager.LookAndFeelInfo laf) {
try {
UIManager.setLookAndFeel(laf.getClassName());
Container c = rootAncestor;
SwingUtilities.updateComponentTreeUI(c);
} catch (Exception e) {
System.err.println("Failed to install " + laf.getName() + "L&F\n" + e);
}
}
/**
* Creates a menu that allows the user to switch between the
* available look-and-feels.
*
* @return The look-and-feel menu.
*/
protected JMenu createLAFMenu() {
JMenu lafMenu = new JMenu("LookAndFeel");
lafMenu.setMnemonic(KeyEvent.VK_O);
setHelpID(lafMenu, "Menu.LAF.helpID");
setStatusText(lafMenu, "Selects a Look-and-Feel for the example.");
ButtonGroup lafGroup = new ButtonGroup();
ItemListener menuListener = new ItemListener()
{
Override
public void itemStateChanged(ItemEvent evt) {
JRadioButtonMenuItem button = (JRadioButtonMenuItem) evt.getSource();
if (button.isSelected()) {
UIManager.LookAndFeelInfo selectedLF =
(UIManager.LookAndFeelInfo) button.getClientProperty("UIKey");
setLookAndFeel(selectedLF);
}
}
};
// Get the list of installed look-and-feels from the Swing UIManager.
UIManager.LookAndFeelInfo[] installedLAFs =
UIManager.getInstalledLookAndFeels();
// Add the Kunststoff look-and-feel to the list.
List<UIManager.LookAndFeelInfo> lafs = new ArrayList<UIManager.LookAndFeelInfo>();
lafs.addAll(Arrays.asList(installedLAFs));
lafs.add(new UIManager.LookAndFeelInfo("Kunststoff",
"com.incors.plaf.kunststoff.KunststoffLookAndFeel"));
// Populate the menu with the LAFs.
for (UIManager.LookAndFeelInfo lafInfo : lafs) {
String lafName = lafInfo.getName();
String lafClassName = lafInfo.getClassName();
boolean lafSupported = false;
// We need to verify that the LAF actually exists and is supported.
if (!Version.isJava9OrLater()) {
try {
Class<?> lafClass = Class.forName(lafClassName);
LookAndFeel laf = (LookAndFeel) lafClass.getDeclaredConstructor().newInstance();
lafSupported = laf.isSupportedLookAndFeel();
} catch (Throwable t) {
LOGGER.debug("AbstractExample.createLAFMenu: ", t);
}
} else {
// For Java 9 or later, it is no longer acceptable to use core reflection to instantiate
// LookAndFeel instances, since they are internal to the JDK. We must instead call
// method UIManager.createLookAndFeel. An UnsupportedLookAndFeelException being thrown
// indicates the LookAndFeel is not supported by the underlying platform.
// See: https://bugs.openjdk.java.net/browse/JDK-8178826
try {
Method method = UIManager.class.getMethod("createLookAndFeel", String.class);
method.invoke(null, lafInfo.getName());
lafSupported = true; // Since no UnsupportedLookAndFeelException was thrown.
} catch (Throwable t) {
LOGGER.debug("AbstractExample.createLAFMenu: ", t);
}
}
if (lafSupported) {
JRadioButtonMenuItem rb = new JRadioButtonMenuItem(lafName);
lafMenu.add(rb);
rb.setSelected(UIManager.getLookAndFeel().getName().equals(lafName));
rb.putClientProperty("UIKey", lafInfo);
rb.addItemListener(menuListener);
lafGroup.add(rb);
}
}
return lafMenu;
}
// =========================================
// Help System
// =========================================
/**
* An empty help properties dictionary.
*/
private static final HashMap<String,Object> EMPTY_HELP_PROPERTIES =
new HashMap<String,Object>();
/**
* Returns the dictionary of Help System properties for this example. Each key
* and its corresponding value must be a string. This default implementation
* returns an empty dictionary, indicating that the example has no Help System.
* <p>
* The dictionary should define the following keys in order to setup a
* a basic Help System for the example:
* <ul>
* <li>
* HelpSet.name - The name of the help set for the example. This should be
* the name of the .HS file, relative to the location of the example's main
* class file.</li>
* <li>
* HelpSet.mainTopic - The top-level help ID for the entire example.</li>
* <li>
* Gantt.helpID - The help ID for the entire Gantt chart.</li>
* <li>
* Menu.helpID - The help ID for the entire menu bar.</li>
* <li>
* Menu.LAF.helpID - The help ID for the LAF Menu.</li>
* <li>
* Menu.Help.helpID - The help ID for the Help Menu.</li>
* <li>
* Toolbar.helpID - The help ID for the entire toolbar.</li>
* <li>
* Customizer.helpID - The help ID for the entire customizer panel.</li>
* </ul>
*
* @return The dictionary of help system properties.
*/
protected Map<String,Object> getHelpProperties() {
return EMPTY_HELP_PROPERTIES;
}
/**
* Returns the value of the specified Help System property. The following
* pre-defined keys should be used to setup a basic Help System for the
* example:
* <ul>
* <li>
* HelpSet.name - The name of the help set for the example. This should be
* the name of the .HS file, relative to the location of the example's main
* class file.</li>
* <li>
* HelpSet.mainTopic - The top-level help ID for the entire example.</li>
* <li>
* Gantt.helpID - The help ID for the entire Gantt chart.</li>
* <li>
* Menu.helpID - The help ID for the entire menu bar.</li>
* <li>
* Menu.LAF.helpID - The help ID for the LAF Menu.</li>
* <li>
* Menu.Help.helpID - The help ID for the Help Menu.</li>
* <li>
* Toolbar.helpID - The help ID for the entire toolbar.</li>
* <li>
* Customizer.helpID - The help ID for the entire customizer panel.</li>
* </ul>
*
* @param key The help ID.
* @return The value of the help property.
*/
protected String getHelpProperty(String key) {
Map<String,Object> props = getHelpProperties();
Object objVal = props.get(key);
return (objVal instanceof String)
? (String) objVal
: null;
}
/**
* Sets the helpID for a component, based upon the specified Help System
* property.
*
* @param comp The component.
* @param key The help ID.
*/
protected void setHelpID(Component comp, String key) {
if (!hasHelpSystem() || key == null) {
return;
}
String id = getHelpProperty(key);
if (id != null) {
getHelpSystem().setHelpIDString(comp, id);
}
}
/**
* Sets the helpID for a menu item, based upon the specified Help System
* property.
*
* @param item The menu item.
* @param key The help ID.
*/
protected void setHelpID(MenuItem item, String key) {
if (!hasHelpSystem() || key == null) {
return;
}
String id = getHelpProperty(key);
if (id != null) {
getHelpSystem().setHelpIDString(item, id);
}
}
/**
* Returns the class name of the Java Help based help system that should be
* used if possible. The default is <code>"shared.help.JavaHelpSystem"</code>.
* <p>
* This method can be overridden to return the name of a subclass of
* <code>shared.help.JavaHelpSystem</code>.
*/
protected String getJavaHelpSystemClassName() {
return "shared.help.JavaHelpSystem";
}
/**
* Creates the help system.
* <p>
* This method creates an instance of the class designated by
* <code>getJavaHelpSystemClassName()</code> if possible, or an instance of
* <code>shared.help.NoHelpSystem</code> otherwise.
*/
protected HelpSystem createHelpSystem() {
String className = getJavaHelpSystemClassName();
try {
Class<?> clazz = Class.forName(className);
return (HelpSystem) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (ExceptionInInitializerError e) {
e.getException().printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
} catch (NoClassDefFoundError e) {
// Some dependency class is not found in the CLASSPATH.
// This happens when jhbasic.jar is not in the CLASSPATH.
} catch (LinkageError e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return new NoHelpSystem();
}
/**
* Initializes the Help System for the example.
*/
protected void initHelpSystem() {
String helpSetName = getHelpProperty("HelpSet.name");
if (helpSetName != null) {
helpSystem = createHelpSystem();
helpSystem.init(helpSetName, getClass());
} else {
helpSystem = new NoHelpSystem();
}
}
/**
* Returns the help system for the example.
*
* @return The help system.
*/
public HelpSystem getHelpSystem() {
return helpSystem;
}
/**
* Returns true if the example has a Help System.
*
* @return Whether the example has a help system.
*/
public boolean hasHelpSystem() {
return !(getHelpSystem() instanceof NoHelpSystem);
}
/**
* Initializes the Help System actions.
*
* @return Whether the example has a help system that was successfully initialized.
*/
private boolean initHelpActions() {
if (!hasHelpSystem()) {
return false;
}
if (helpAction == null) {
final ActionListener helpActionListener =
getHelpSystem().createShowHelpActionListener();
helpAction = new IlvAction("Help Topics",
null,
KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0),
"Help",
"Displays help about this example.") {
Override
public void actionPerformed(ActionEvent event) {
helpActionListener.actionPerformed(event);
}
};
helpAction.setIcon(AbstractExample.class, "images/help.gif");
}
if (contextHelpAction == null) {
final ActionListener contextHelpActionListener =
getHelpSystem().createShowContextSensitiveHelpActionListener();
contextHelpAction =
new IlvAction("What's This?",
null,
KeyStroke.getKeyStroke(KeyEvent.VK_F1,
KeyEvent.SHIFT_DOWN_MASK),
"Context Sensitive Help",
"Displays help about this example.")
{
Override
public void actionPerformed(ActionEvent event) {
contextHelpActionListener.actionPerformed(event);
}
};
contextHelpAction.setIcon(AbstractExample.class, "images/cshelp.gif");
}
return true;
}
/**
* Creates the Help menu. This method returns <code>null</code> if the example
* does not have a Help System.
*
* @return The help menu.
*/
public JMenu createHelpMenu() {
if (!initHelpActions()) {
return null;
}
JMenu menu = new JMenu("Help");
menu.setMnemonic(KeyEvent.VK_H);
setHelpID(menu, "Menu.Help.helpID");
setStatusText(menu, "Displays help about this example.");
JMenuItem helpItem = addAction(menu, helpAction);
setHelpID(helpItem, "HelpSet.mainTopic");
addAction(menu, contextHelpAction);
return menu;
}
/**
* Populates the specified toolbar with the Help System actions.
*
* @param toolbar The toolbar.
*/
protected void populateToolBarHelpActions(JToolBar toolbar) {
if (!initHelpActions()) {
return;
}
toolbar.addSeparator();
JButton helpButton = addAction(toolbar, helpAction);
setHelpID(helpButton, "HelpSet.mainTopic");
addAction(toolbar, contextHelpAction);
}
// =========================================
// Colors
// =========================================
/**
* Shows a modal color-chooser dialog and blocks until the dialog is hidden.
* If the user presses the "OK" button, this method hides/disposes the
* dialog and returns the selected color. If the user presses the "Cancel"
* button or closes the dialog without pressing "OK", this method
* hides/disposes the dialog and returns <code>null</code>.
*
* @param title The title of the dialog.
* @param initialColor The initial color set when the color-chooser is shown.
* @return The color.
*/
protected Color chooseColor(String title, Color initialColor) {
int alpha = initialColor.getAlpha();
Color newColor = IlvJColorChooser.showDialog(this, title, initialColor);
if (newColor != null) {
if (newColor.getAlpha() != alpha) {
newColor = new Color(newColor.getRed(),
newColor.getGreen(),
newColor.getBlue(),
alpha);
}
}
return newColor;
}
/**
* <code>MenuInfo</code> is an inner class used for displaying information on
* menus and menu items. The status text of the menu item is displayed in the
* status bar. This class should be registered as a
* <code>MenuDragMouseListener</code>
* and as a <code>MouseMotionListener</code> with <code>JMenuItem</code>s. It
* should be registered as a <code>MenuDragMouseListener</code> and as a
* <code>MenuListener</code> with <code>JMenu</code>s.
*/
class MenuInfo implements MenuDragMouseListener,
MenuListener,
MouseMotionListener {
// Text used to clear the status bar.
private static final String NULL_TEXT = " ";
// The current text being displayed.
private String currentText = NULL_TEXT;
// Clears the status bar.
private void clearStatus() {
JLabel statusBar = getStatusBar();
if (statusBar == null) {
return;
}
if (!NULL_TEXT.equals(currentText)) {
currentText = NULL_TEXT;
statusBar.setText(NULL_TEXT);
}
}
// Sets the status bar based on the source of the specified event.
private void setStatus(EventObject e) {
JLabel statusBar = getStatusBar();
if (statusBar == null) {
return;
}
Object source = e.getSource();
if (source instanceof JComponent) {
String str = getStatusText((JComponent) source);
if (str == null || str.equals("")) {
clearStatus();
} else if (!currentText.equals(str)) {
currentText = str;
statusBar.setText(str);
}
} else {
clearStatus();
}
}
// A JMenu has been cancelled. Clears the status bar.
Override
public void menuCanceled(MenuEvent e) {
clearStatus();
}
// A JMenu has been deselected. Clears the status bar.
Override
public void menuDeselected(MenuEvent e) {
clearStatus();
}
// A JMenu has been selected. Sets the status bar to its tooltip text,
// if any.
Override
public void menuSelected(MenuEvent e) {
setStatus(e);
}
// The user has moved the mouse into a menu element. Sets the status bar to
// its tooltip text, if any.
Override
public void menuDragMouseEntered(MenuDragMouseEvent e) {
setStatus(e);
}
// The user has moved the mouse out of a menu element. Clears the status
// bar.
Override
public void menuDragMouseExited(MenuDragMouseEvent e) {
clearStatus();
}
// The user has dragged the mouse within a menu element. Sets the status
// bar to its tooltip text, if any.
Override
public void menuDragMouseDragged(MenuDragMouseEvent e) {
setStatus(e);
}
// The user has moved the mouse within a menu element. Sets the status bar
// to its tooltip text, if any.
Override
public void mouseMoved(MouseEvent e) {
setStatus(e);
}
Override
public void menuDragMouseReleased(MenuDragMouseEvent e) {
}
Override
public void mouseDragged(MouseEvent e) {
}
}
/**
* <code>ToolTipUI</code> is an inner class that replaces the standard LAF
* delegate for all application tooltips. This LAF serves the purpose of
* displaying accelerator bindings correctly. As of JDK 1.2.2, the standard
* Metal tooltip UI displays accelerator bindings incorrectly
* (see JavaSoft bug #4306876).
* First, the Metal tooltip UI attempts to derive the accelerator binding
* automatically from the underlying component. Unfortunately, this only
* works when the underlying component has a single keystroke binding.
* Second, the Metal tooltip UI displays non-ASCII keystrokes incorrectly,
* such as VK_DELETE, and so on.
* This tooltip UI corrects the first problem by parsing the underlying
* component's tooltip text at the first vertical bar. All text before the
* delimiter is considered to be the normal tooltip text and all text
* following the delimiter is considered to be the accelerator binding text.
*/
public static class ToolTipUI extends BasicToolTipUI {
private static ToolTipUI sharedInstance = new ToolTipUI();
private static final int HORIZONTAL_MARGIN = 3;
private static final int VERTICAL_MARGIN = 2;
private static final String ACCELERATOR_FONT_KEY = "ACCEL_FONT";
public static final String TIP_DELIMITER = "|";
private static final int PAD_SPACE_BETWEEN_STRINGS = 15;
/**
* Creates a new <code>AbstractExample.ToolTipUI</code>.
*/
public ToolTipUI() {
}
public static ComponentUI createUI(JComponent c) {
return sharedInstance;
}
Override
public void installUI(JComponent c) {
super.installUI(c);
Font f = c.getFont();
// Store a smaller version of the tooltip font as a client property of
// the JToolTip itself. This will be used to display the accelerator
// text to the right of the normal tooltip text.
Font acceleratorFont = new Font(f.getName(), f.getStyle(), f.getSize() - 2);
c.putClientProperty(ACCELERATOR_FONT_KEY, acceleratorFont);
}
/**
* Returns the font used to display the accelerator keystroke binding from
* the specified <code>JToolTip</code> component.
*
* @param c The component.
* @return The accelerator font.
*/
public Font getAcceleratorFont(JComponent c) {
return (Font) c.getClientProperty(ACCELERATOR_FONT_KEY);
}
/**
* Returns the tooltip text from the specified <code>JToolTip</code>
* component.
*
* @param c The component.
* @return The tooltip text.
*/
public String getTipText(JComponent c) {
String text = ((JToolTip) c).getTipText();
if (text == null || text.equals("")) {
text = "";
}
int index = text.indexOf(TIP_DELIMITER);
if (index >= 0) {
text = text.substring(0, index);
}
return text;
}
/**
* Returns the text representation of the accelerator keystroke binding
* from the specified <code>JToolTip</code> component.
*
* @param c The component.
* @return The accelerator text.
*/
public String getAcceleratorText(JComponent c) {
String text = ((JToolTip) c).getTipText();
if (text == null || text.equals("")) {
text = "";
}
int index = text.indexOf(TIP_DELIMITER);
if (index < 0) {
return "";
}
return text.substring(index + 1);
}
Override
public void paint(Graphics g, JComponent c) {
Font font = c.getFont();
FontMetrics fm = c.getFontMetrics(font);
Dimension size = c.getSize();
Insets insets = c.getInsets();
g.setColor(c.getBackground());
g.fillRect(0, 0, size.width, size.height);
g.setColor(c.getForeground());
g.setFont(font);
String tipText = getTipText(c);
View v = (View) c.getClientProperty("html");
if (v != null) {
Rectangle paintTextR = c.getBounds();
paintTextR.x += insets.left;
paintTextR.y += insets.top;
paintTextR.width -= insets.left + insets.right;
paintTextR.height -= insets.top + insets.bottom;
v.paint(g, paintTextR);
} else {
g.drawString(tipText, insets.left + HORIZONTAL_MARGIN,
insets.top + VERTICAL_MARGIN + fm.getAscent());
}
String accelText = getAcceleratorText(c);
if (!accelText.equals("")) {
Font acceleratorFont = getAcceleratorFont(c);
g.setFont(acceleratorFont);
g.drawString(accelText,
insets.left + HORIZONTAL_MARGIN
+ SwingUtilities.computeStringWidth(fm, tipText)
+ PAD_SPACE_BETWEEN_STRINGS,
insets.top + VERTICAL_MARGIN + fm.getAscent());
}
}
Override
public Dimension getPreferredSize(JComponent c) {
Font font = c.getFont();
Insets insets = c.getInsets();
Dimension prefSize = new Dimension(insets.left + insets.right,
insets.top + insets.bottom);
String text = getTipText(c);
if (!text.equals("")) {
View v = (c != null) ? (View) c.getClientProperty("html") : null;
if (v != null) {
prefSize.width += (int) v.getPreferredSpan(View.X_AXIS);
prefSize.height += (int) v.getPreferredSpan(View.Y_AXIS);
} else {
FontMetrics fm = c.getFontMetrics(font);
prefSize.width += SwingUtilities.computeStringWidth(fm, text)
+ (2 * HORIZONTAL_MARGIN);
prefSize.height += fm.getHeight() + (2 * VERTICAL_MARGIN);
}
}
String accelText = getAcceleratorText(c);
if (!accelText.equals("")) {
FontMetrics accelFM = c.getFontMetrics(getAcceleratorFont(c));
prefSize.width += SwingUtilities.computeStringWidth(accelFM, accelText)
+ PAD_SPACE_BETWEEN_STRINGS;
}
return prefSize;
}
}
}