/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad 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.
*
* Nomad 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 Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Created on Oct 29, 2006
*/
package net.sf.nmedit.nomad.core.swing.explorer;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.plaf.TreeUI;
import javax.swing.plaf.metal.MetalTreeUI;
import javax.swing.tree.TreePath;
import net.sf.nmedit.nmutils.Platform;
import net.sf.nmedit.nomad.core.Nomad;
import net.sf.nmedit.nomad.core.swing.explorer.helpers.ExplorerCellRenderer;
import net.sf.nmedit.nomad.core.swing.explorer.helpers.TreeDynamicTreeExpansion;
public class ExplorerTreeUI extends MetalTreeUI
{
private static Icon getIcon(String name)
{
URL url = ExplorerTreeUI.class.getClassLoader().getResource( "swing/browser/"+name);
return url == null ? null : new ImageIcon(url);
}
private static Icon getIcon2(String name)
{
URL url = Nomad.sharedInstance().getClass()
.getResource("/icons/tango/16x16/"+name);
return url == null ? null : new ImageIcon(url);
}
static Icon openIcon = getIcon("node-state-opened.png");
static Icon closedIcon = getIcon("node-state-closed.png");
static Icon openIconHov = getIcon("node-state-opened-hovered.png");
static Icon closedIconHov = getIcon("node-state-closed-hovered.png");
public static final Icon DefaultFolderOpenedIcon = getIcon2("status/folder-open.png");
public static final Icon DefaultFolderClosedIcon = getIcon2("places/folder.png");
public static final Icon DefaultFileIcon = getIcon2("mimetypes/text-x-generic.png");
//Icon leafIcon = new ImageIcon("net/sf/nmedit/nomad/cbrowser/images/...");
public final static Color defaultSelectionBackground = Color.decode("#A8A8A8");
private Color backgroundSelectionColor = null;
private Color alternatingRowColor = null;
public void installUI( JComponent c )
{
JTree tree = (JTree) c;
tree.putClientProperty("JTree.lineStyle", "None");
super.installUI( c );
c.setBackground(Color.WHITE);
alternatingRowColor = new Color(0xF0F0FF);
tree.setRootVisible(false);
ExplorerCellRenderer tcr = new ExplorerCellRenderer();
tree.setCellRenderer(tcr);
tcr.setOpenIcon(DefaultFolderOpenedIcon);
tcr.setClosedIcon(DefaultFolderClosedIcon);
tcr.setLeafIcon(DefaultFileIcon);
tree.addTreeExpansionListener(new TreeDynamicTreeExpansion(tree));
tree.setShowsRootHandles(true);
tree.setScrollsOnExpand(false);
}
protected void installDefaults()
{
//tree.putClientProperty("Tree.selectionBackground", defaultSelectionBackground);
super.installDefaults();
backgroundSelectionColor = defaultSelectionBackground;//UIManager.getColor("Tree.selectionBackground");
setExpandedIcon(openIcon);
setCollapsedIcon(closedIcon);
}
public void updateScrollPosition(Point location) {
Rectangle visible = tree.getVisibleRect();
int wScroll = Math.min(visible.width / 3, 40);
int hScroll = Math.min(visible.height / 3, 40);
// System.out.println("w " + wScroll + " h " + hScroll);
Rectangle scrollTo = new Rectangle(location);
// if (location.x < (visible.x + wScroll))
// scrollTo.x = Math.max(location.x - wScroll, 0);
// if (location.x > (visible.x + visible.width - wScroll))
// scrollTo.x = location.x + wScroll;
//
if (location.y < (visible.y + hScroll))
scrollTo.y = Math.max(location.y - hScroll, 0);
if (location.y > (visible.y + visible.height - hScroll))
scrollTo.y = location.y + hScroll;
// System.out.println("location " + location.x + " " + location.y + " visible " + visible.x + " " + visible.y + " scrollto " + scrollTo.x + " " + scrollTo.y);
tree.scrollRectToVisible(scrollTo);
}
protected void paintRow(Graphics g, Rectangle clipBounds,
Insets insets, Rectangle bounds, TreePath path,
int row, boolean isExpanded,
boolean hasBeenExpanded, boolean isLeaf)
{
// Don't paint the renderer if editing this row.
if(editingComponent != null && editingRow == row)
return;
int h = tree.getRowHeight();
if (tree.isRowSelected(row))
{
g.setColor(backgroundSelectionColor);
g.fillRect(clipBounds.x, h*row, clipBounds.width, h );
/*if(shouldPaintExpandControl(path, row, isExpanded,
hasBeenExpanded, isLeaf)) {
*/
// }
}
else if (alternatingRowColor != null && row%2==0)
{
g.setColor(alternatingRowColor);
g.fillRect(clipBounds.x, h*row, clipBounds.width, h );
}
paintExpandControl(g, bounds, insets, bounds,
path, row, isExpanded,
hasBeenExpanded, isLeaf);
super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded,
hasBeenExpanded, isLeaf);
}
public void paint(Graphics g, JComponent c)
{
super.paint(g, c);
Insets i = tree.getInsets();
int rc = getRowCount(tree);
int rh = getRowHeight();
if (rh < 1)
return;
if (rc%2==1)
rc++;
int y = i.top + rc*rh;
int bottom = tree.getHeight()-i.bottom;
g.setColor(alternatingRowColor);
//int r = tree.getWidth()-i.left-i.right;
while (y<bottom)
{
g.fillRect(i.left, y, tree.getWidth(), rh);
y+=rh*2;
}
}
ExpandControlHoverEffect eche = new ExpandControlHoverEffect();
protected void installListeners()
{
tree.addMouseListener(eche);
tree.addMouseMotionListener(eche);
super.installListeners();
}
protected void uninstallListeners()
{
tree.removeMouseMotionListener(eche);
tree.removeMouseListener(eche);
super.uninstallListeners();
}
protected void paintExpandControl(Graphics g,
Rectangle clipBounds, Insets insets,
Rectangle bounds, TreePath path,
int row, boolean isExpanded,
boolean hasBeenExpanded,
boolean isLeaf) {
//Object value = path.getLastPathComponent();
// Draw icons if not a leaf and either hasn't been loaded,
// or the model child count is > 0.
//if (!isLeaf && (treeModel.getChildCount(value) > 0))
{
if (hoveredRow==row)
{
setExpandedIcon(openIconHov);
setCollapsedIcon(closedIconHov);
}
else
{
setExpandedIcon(openIcon);
setCollapsedIcon(closedIcon);
}
super.paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded,
hasBeenExpanded, isLeaf);
}/*
else {
super.paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded,
hasBeenExpanded, isLeaf);
}*/
}
public Rectangle getPathBounds(JTree tree, TreePath path) {
if(tree != null && treeState != null) {
Insets i = tree.getInsets();
Rectangle bounds = treeState.getBounds(path, null);
if(bounds != null && i != null) {
bounds.x += i.left;
bounds.y += i.top;
// we use the full row width instead of only the label bounds
bounds.width = tree.getWidth()-i.right-bounds.x;
//treeState.getBounds(path, bounds); // write back
}
return bounds;
}
return null;
}
public TreePath getClosestPathForLocation(JTree tree, int x, int y)
{
Insets insets = tree.getInsets();
int maxY = tree.getRowCount()*tree.getRowHeight()-insets.top;
if (y>maxY)
return null;
return super.getClosestPathForLocation(tree, x, y);
}
protected void selectPathForEvent(TreePath path, MouseEvent event) {
if (getSelectionModel().isPathSelected(path) && Platform.isPopupTrigger(event)) {
// we don't want to deselect with right clicking
return;
}
super.selectPathForEvent(path, event);
}
protected boolean isToggleSelectionEvent(MouseEvent event) {
return Platform.isToggleSelectionEvent(event);
}
int hoveredRow = -1;
int hovx = 0;
int hovy = 0;
private static class ExpandControlHoverEffect
implements MouseMotionListener, MouseListener
{
public void mousePressed(MouseEvent e)
{
forwardMouseEvent(e);
Component c = e.getComponent();
if (!(c instanceof JTree)) return;
JTree tree = (JTree) c;
// avoid having a mouse press select, do it on mouse release on macosx
if (Platform.isFlavor(Platform.OS.MacOSFlavor)) {
Point p = e.getPoint();
TreePath path = tree.getClosestPathForLocation(p.x, p.y);
if (path != null && tree.isPathSelected(path)) {
e.consume();
}
}
}
public void mouseReleased(MouseEvent e)
{
forwardMouseEvent(e);
}
// ExpandControlHoverEffect
public void mouseMoved(MouseEvent e)
{
Component c = e.getComponent();
if (!(c instanceof JTree)) return;
JTree tree = (JTree) c;
TreeUI treeUI = tree.getUI();
if (!(treeUI instanceof ExplorerTreeUI)) return;
ExplorerTreeUI etUI = (ExplorerTreeUI) treeUI;
int lastRow = etUI.hoveredRow;
TreePath tp =
etUI.getClosestPathForLocation(tree, e.getX(), e.getY());
if (tp == null)
return ;
int row = tree.getUI().getRowForPath(tree,tp);
if (etUI.isLocationInExpandControl(
row, tp.getPathCount()-1, e.getX(), e.getY()
))
{
etUI.hoveredRow = row;
}
else
{
etUI.hoveredRow=-1;
}
if (etUI.hoveredRow!=lastRow)
{
tree.repaint(etUI.hovx-15, etUI.hovy-15,30,30);
tree.repaint(e.getX()-15, e.getY()-15,30,30);
etUI.hovx = e.getX();
etUI.hovy = e.getY();
}
}
public void mouseDragged(MouseEvent e)
{
// nothing
}
public void mouseClicked(MouseEvent e)
{
forwardMouseEvent(e);
}
public void mouseEntered(MouseEvent e)
{
forwardMouseEvent(e);
}
public void mouseExited(MouseEvent e)
{
forwardMouseEvent(e);
}
public void forwardMouseEvent(MouseEvent e)
{
Component c = e.getComponent();
if (!(c instanceof ExplorerTree))
return;
ExplorerTree tree = (ExplorerTree) c;
TreePath tp = tree.getPathForLocation(e.getX(), e.getY());
if (tp == null)
return;
Object o = tp.getLastPathComponent();
if (o instanceof ETreeNode)
{
((ETreeNode)o).processEvent(e);
}
}
}
}