/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.framework.internal;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.border.Border;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.eclipse.persistence.tools.workbench.framework.app.AbstractPreferencesNode;
import org.eclipse.persistence.tools.workbench.framework.app.PreferencesNode;
import org.eclipse.persistence.tools.workbench.uitools.cell.SimpleTreeCellRenderer;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
/**
*
*/
final class PreferencesNavigatorView {
/** tree of preferences nodes */
private JTree tree;
/** root node of preferences nodes */
private AbstractPreferencesNode rootNode;
/** simple panel holding the tree */
private JPanel panel;
// ********** constructors/initialization **********
/**
* Construct a navigator for the specified root preferences node.
*/
public PreferencesNavigatorView(AbstractPreferencesNode root) {
super();
this.initialize(root);
}
private void initialize(AbstractPreferencesNode root) {
this.rootNode = root;
this.tree = this.buildTree(root);
this.panel = this.buildPanel();
expandAll(this.rootNode);
}
/**
* Expands the given node. The children will be recursively expanded.
*/
private void expandAll(TreeNode node) {
this.tree.expandPath(buildPath(node));
for (Enumeration enumeration = node.children(); enumeration.hasMoreElements(); ) {
TreeNode child = (TreeNode) enumeration.nextElement();
if (!child.isLeaf()) {
expandAll(child);
}
}
}
private TreePath buildPath(TreeNode node) {
Vector paths = new Vector();
do {
paths.add(0, node);
node = node.getParent();
} while (node != null);
return new TreePath(paths.toArray());
}
/**
* build a tree using the specified root node
*/
private JTree buildTree(PreferencesNode root) {
JTree tree = new JTree(root);
tree.setShowsRootHandles(true);
tree.setRootVisible(false);
tree.setCellRenderer(new PreferencesTreeCellRenderer());
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addKeyListener(buildF1KeyListener());
tree.addMouseListener(buildMouseListener());
return tree;
}
/**
* The following Mouse and Key Listeners are customized so that "F1" and right-click context sensitive
* help will work with the preferences node tree on the preferences dialog.
*/
private MouseListener buildMouseListener() {
return new LocalMouseListener(getRootNode().resourceRepository().getString("HELP_POPUP"));
}
private KeyListener buildF1KeyListener() {
return new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// show help for the first selected node
if (e.getKeyCode() == KeyEvent.VK_F1) {
AbstractPreferencesNode node = (AbstractPreferencesNode)getTree().getLastSelectedPathComponent();
if (node != null) {
node.showHelp();
}
}
}
};
}
private JPanel buildPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEtchedBorder());
panel.setMinimumSize(new Dimension(0, 0));
JScrollPane scrollPane = new JScrollPane(this.tree);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.getHorizontalScrollBar().setUnitIncrement(10);
scrollPane.setBorder(null);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
// ********** listeners **********
/**
* Add a listener that will be notified of tree selection events.
*/
synchronized void addTreeSelectionListener(TreeSelectionListener listener) {
this.tree.addTreeSelectionListener(listener);
}
/**
* Remove the specified listener.
*/
synchronized void removeTreeSelectionListener(TreeSelectionListener listener) {
this.tree.removeTreeSelectionListener(listener);
}
// ********** queries **********
Component getComponent() {
return this.panel;
}
Component initialFocusComponent() {
return this.tree;
}
JTree getTree() {
return this.tree;
}
AbstractPreferencesNode getRootNode() {
return this.rootNode;
}
// ********** behavior **********
void selectFirstChild() {
this.tree.setSelectionPath(buildPath(this.rootNode.getChildAt(0)));
}
// ********** inner classes **********
/**
* This renderer creates a new PreferencesTreeCellRenderer each time the
* renderer is requested. It is required when JAWS is running.
*/
private static class PreferencesTreeCellRenderer implements TreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
NodeRenderer renderer = new NodeRenderer();
return renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
}
/**
* This renderer paints a label with no icon and text derived
* from the the preferences node.
*/
private static class NodeRenderer extends SimpleTreeCellRenderer {
// this border improves the readability a bit
private static final Border BORDER = BorderFactory.createEmptyBorder(0, 2, 0, 0);
public NodeRenderer() {
super();
}
/**
* @see org.eclipse.persistence.tools.workbench.uitools.cell.SimpleTreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean)
*/
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
this.setBorder(BORDER);
return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
}
/**
* @see org.eclipse.persistence.tools.workbench.uitools.cell.SimpleTreeCellRenderer#buildIcon(Object)
*/
protected Icon buildIcon(Object value) {
return null;
}
/**
* @see org.eclipse.persistence.tools.workbench.uitools.cell.SimpleTreeCellRenderer#buildText(Object)
*/
protected String buildText(Object value) {
return ((PreferencesNode) value).displayString();
}
}
/**
* This listener will respond to a mouse pop-up trigger and
* add the Help menu to the tree. Selecting the Help menu
* item will trigger the selected tree items help
* topic to be displayed.
*/
private class LocalMouseListener extends MouseAdapter {
/** the Help pop-up menu */
PopupMenu popupMenu;
/**
* coordinates where mouse event happened help onto so that we will
* know which item in the tree the mouse was clicked on.
*/
int x;
int y;
// ********** constructor/initialization **********
LocalMouseListener(String menuItemLabel) {
super();
this.initialize(menuItemLabel);
}
/**
* build the pop-up menu that will be added to the tree selection
*/
private void initialize(String menuItemLabel) {
MenuItem item = new MenuItem(menuItemLabel);
item.addActionListener(this.buildMenuItemListener());
this.popupMenu = new PopupMenu();
this.popupMenu.add(item);
}
/**
* this is the action that is executed if the user selects
* the Help menu item from the pop-up menu
*/
private ActionListener buildMenuItemListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
// display help for the component stashed away when the pop-up menu was first displayed
LocalMouseListener.this.showHelp();
// not sure if this is needed...
LocalMouseListener.this.popupMenu.getParent().remove(LocalMouseListener.this.popupMenu);
}
};
}
// ********** MouseListener implementation **********
public void mousePressed(MouseEvent e) {
handleMouseEvent(e);
}
public void mouseReleased(MouseEvent e) {
handleMouseEvent(e);
}
private void handleMouseEvent(MouseEvent e) {
if ( ! e.isPopupTrigger()) {
return;
}
getTree().add(this.popupMenu);
this.x = e.getX();
this.y = e.getY();
this.popupMenu.show(getTree(), e.getX(), e.getY());
}
// ********** behavior **********
/**
* show help for the pop-up menu's selected component
*/
void showHelp() {
int row = getTree().getRowForLocation(this.x, this.y);
if (row != -1) {
TreePath path = PreferencesNavigatorView.this.tree.getPathForRow(row);
TreePath[] selectedPaths = PreferencesNavigatorView.this.tree.getSelectionPaths();
if (selectedPaths == null || ! CollectionTools.contains(selectedPaths, path)) {
PreferencesNavigatorView.this.tree.setSelectionPath(path);
}
AbstractPreferencesNode node = (AbstractPreferencesNode)getTree().getLastSelectedPathComponent();
if (node != null) {
node.showHelp();
}
}
}
}
}