/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
|
* or http://forgerock.org/license/CDDLv1.0.html.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at legal-notices/CDDLv1_0.txt.
|
* If applicable, add the following below this CDDL HEADER, with the
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
* information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2008 Sun Microsystems, Inc.
|
* Portions Copyright 2014 ForgeRock AS
|
*/
|
|
package org.opends.guitools.controlpanel.ui.components;
|
|
import static com.forgerock.opendj.util.OperatingSystem.isMacOS;
|
|
import java.awt.Graphics;
|
import java.awt.Insets;
|
import java.awt.Rectangle;
|
import java.awt.event.InputEvent;
|
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseListener;
|
import java.util.ArrayList;
|
import java.util.HashSet;
|
import java.util.Set;
|
|
import javax.swing.JPopupMenu;
|
import javax.swing.JTree;
|
import javax.swing.tree.TreePath;
|
|
import org.opends.guitools.controlpanel.ui.renderer.TreeCellRenderer;
|
|
/**
|
* The tree that is used in different places in the Control Panel (schema
|
* browser, index browser or the LDAP entries browser). It renders in a
|
* different manner than the default tree (selection takes the whole width
|
* of the tree, in a similar manner as happens with trees in Mac OS).
|
*
|
*/
|
public class CustomTree extends JTree
|
{
|
private static final long serialVersionUID = -8351107707374485555L;
|
private Set<MouseListener> mouseListeners;
|
private JPopupMenu popupMenu;
|
private final int MAX_ICON_HEIGHT = 18;
|
|
/**
|
* Internal enumeration used to translate mouse events.
|
*
|
*/
|
private enum NewEventType
|
{
|
MOUSE_PRESSED, MOUSE_CLICKED, MOUSE_RELEASED
|
};
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void paintComponent(Graphics g)
|
{
|
int[] selectedRows = getSelectionRows();
|
if (selectedRows == null)
|
{
|
selectedRows = new int[] {};
|
}
|
Insets insets = getInsets();
|
int w = getWidth( ) - insets.left - insets.right;
|
int h = getHeight( ) - insets.top - insets.bottom;
|
int x = insets.left;
|
int y = insets.top;
|
int nRows = getRowCount();
|
for ( int i = 0; i < nRows; i++)
|
{
|
int rowHeight = getRowBounds( i ).height;
|
boolean isSelected = false;
|
for (int j=0; j<selectedRows.length; j++)
|
{
|
if (selectedRows[j] == i)
|
{
|
isSelected = true;
|
break;
|
}
|
}
|
if (isSelected)
|
{
|
g.setColor(TreeCellRenderer.selectionBackground);
|
}
|
else
|
{
|
g.setColor(TreeCellRenderer.nonselectionBackground);
|
}
|
g.fillRect( x, y, w, rowHeight );
|
y += rowHeight;
|
}
|
final int remainder = insets.top + h - y;
|
if ( remainder > 0 )
|
{
|
g.setColor(TreeCellRenderer.nonselectionBackground);
|
g.fillRect(x, y, w, remainder);
|
}
|
|
boolean isOpaque = isOpaque();
|
setOpaque(false);
|
super.paintComponent(g);
|
setOpaque(isOpaque);
|
}
|
|
/**
|
* Sets a popup menu that will be displayed when the user clicks on the tree.
|
* @param popMenu the popup menu.
|
*/
|
public void setPopupMenu(JPopupMenu popMenu)
|
{
|
this.popupMenu = popMenu;
|
}
|
|
/**
|
* Default constructor.
|
*
|
*/
|
public CustomTree()
|
{
|
putClientProperty("JTree.lineStyle", "Angled");
|
// This mouse listener is used so that when the user clicks on a row,
|
// the items are selected (is not required to click directly on the label).
|
// This code tries to have a similar behavior as in Mac OS).
|
MouseListener mouseListener = new MouseAdapter()
|
{
|
private boolean ignoreEvents;
|
/**
|
* {@inheritDoc}
|
*/
|
public void mousePressed(MouseEvent ev)
|
{
|
if (ignoreEvents)
|
{
|
return;
|
}
|
MouseEvent newEvent = getTranslatedEvent(ev);
|
|
if (isMacOS() && ev.isPopupTrigger() &&
|
(ev.getButton() != MouseEvent.BUTTON1))
|
{
|
MouseEvent baseEvent = ev;
|
if (newEvent != null)
|
{
|
baseEvent = newEvent;
|
}
|
int mods = baseEvent.getModifiersEx();
|
mods &= (InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK |
|
InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK);
|
mods |= InputEvent.BUTTON1_DOWN_MASK;
|
final MouseEvent macEvent = new MouseEvent(
|
baseEvent.getComponent(),
|
baseEvent.getID(),
|
System.currentTimeMillis(),
|
mods,
|
baseEvent.getX(),
|
baseEvent.getY(),
|
baseEvent.getClickCount(),
|
false,
|
MouseEvent.BUTTON1);
|
// This is done to select the node when the user does a right
|
// click on Mac OS.
|
notifyNewEvent(macEvent, NewEventType.MOUSE_PRESSED);
|
}
|
|
if (ev.isPopupTrigger() && (popupMenu != null)) {
|
if ((getPathForLocation(ev.getPoint().x, ev.getPoint().y) != null) ||
|
(newEvent != null))
|
{
|
popupMenu.show(ev.getComponent(), ev.getX(), ev.getY());
|
}
|
}
|
if (newEvent != null)
|
{
|
notifyNewEvent(newEvent, NewEventType.MOUSE_PRESSED);
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void mouseReleased(MouseEvent ev)
|
{
|
if (ignoreEvents)
|
{
|
return;
|
}
|
MouseEvent newEvent = getTranslatedEvent(ev);
|
if (ev.isPopupTrigger() && (popupMenu != null) &&
|
!popupMenu.isVisible()) {
|
if ((getPathForLocation(ev.getPoint().x, ev.getPoint().y) != null) ||
|
(newEvent != null))
|
{
|
popupMenu.show(ev.getComponent(), ev.getX(), ev.getY());
|
}
|
}
|
|
if (newEvent != null)
|
{
|
notifyNewEvent(newEvent, NewEventType.MOUSE_RELEASED);
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void mouseClicked(MouseEvent ev)
|
{
|
if (ignoreEvents)
|
{
|
return;
|
}
|
MouseEvent newEvent = getTranslatedEvent(ev);
|
if (newEvent != null)
|
{
|
notifyNewEvent(newEvent, NewEventType.MOUSE_CLICKED);
|
}
|
}
|
|
private void notifyNewEvent(MouseEvent newEvent, NewEventType type)
|
{
|
ignoreEvents = true;
|
// New ArrayList to avoid concurrent modifications (the listeners
|
// could be unregistering themselves).
|
for (MouseListener mouseListener :
|
new ArrayList<MouseListener>(mouseListeners))
|
{
|
if (mouseListener != this)
|
{
|
switch (type)
|
{
|
case MOUSE_RELEASED:
|
mouseListener.mouseReleased(newEvent);
|
break;
|
case MOUSE_CLICKED:
|
mouseListener.mouseClicked(newEvent);
|
break;
|
default:
|
mouseListener.mousePressed(newEvent);
|
}
|
}
|
}
|
ignoreEvents = false;
|
}
|
|
private MouseEvent getTranslatedEvent(MouseEvent ev)
|
{
|
MouseEvent newEvent = null;
|
int x = ev.getPoint().x;
|
int y = ev.getPoint().y;
|
if (getPathForLocation(x, y) == null)
|
{
|
TreePath path = getWidePathForLocation(x, y);
|
if (path != null)
|
{
|
Rectangle r = getPathBounds(path);
|
if (r != null)
|
{
|
int newX = r.x + (r.width / 2);
|
int newY = r.y + (r.height / 2);
|
// Simulate an event
|
newEvent = new MouseEvent(
|
ev.getComponent(),
|
ev.getID(),
|
ev.getWhen(),
|
ev.getModifiersEx(),
|
newX,
|
newY,
|
ev.getClickCount(),
|
ev.isPopupTrigger(),
|
ev.getButton());
|
}
|
}
|
}
|
return newEvent;
|
}
|
};
|
addMouseListener(mouseListener);
|
if (getRowHeight() <= MAX_ICON_HEIGHT)
|
{
|
setRowHeight(MAX_ICON_HEIGHT + 1);
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void addMouseListener(MouseListener mouseListener)
|
{
|
super.addMouseListener(mouseListener);
|
if (mouseListeners == null)
|
{
|
mouseListeners = new HashSet<MouseListener>();
|
}
|
mouseListeners.add(mouseListener);
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void removeMouseListener(MouseListener mouseListener)
|
{
|
super.removeMouseListener(mouseListener);
|
mouseListeners.remove(mouseListener);
|
}
|
|
private TreePath getWidePathForLocation(int x, int y)
|
{
|
TreePath path = null;
|
TreePath closestPath = getClosestPathForLocation(x, y);
|
if (closestPath != null)
|
{
|
Rectangle pathBounds = getPathBounds(closestPath);
|
if (pathBounds != null &&
|
x >= pathBounds.x && x < (getX() + getWidth()) &&
|
y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
|
{
|
path = closestPath;
|
}
|
}
|
return path;
|
}
|
}
|