/**************************************************************************
* Copyright (c) 2007, 2008 Gregory Jordan
*
* This file is part of PhyloWidget.
*
* PhyloWidget 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.
*
* PhyloWidget 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 PhyloWidget. If not, see <http://www.gnu.org/licenses/>.
*/
package org.andrewberman.ui.menu;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import org.andrewberman.ui.Point;
import org.andrewberman.ui.UIRectangle;
import processing.core.PApplet;
import processing.core.PConstants;
public class RadialMenu extends Menu
{
public static final float FADE_DIST_MULTIPLIER = .5f;
public float thetaLo = 0;
public float thetaHi = PConstants.TWO_PI;
float innerRadius;
public float radius;
private VisibleDepthComparator visComp = new VisibleDepthComparator();
protected UIRectangle myRect = new UIRectangle(0, 0, 0, 0);
UIRectangle buffRect = new UIRectangle(0, 0, 0, 0);
AffineTransform buffTransform, mouseTransform;
protected int maxLevelOpen;
public RadialMenu(PApplet p)
{
super(p);
}
public void setOptions()
{
super.setOptions();
setRadii(10, 30);
clickAwayBehavior = Menu.CLICKAWAY_HIDES;
hoverNavigable = false;
clickToggles = true;
autoDim = true;
useCameraCoordinates = true;
modalFocus = true;
useHandCursor = true;
}
@Override
protected void hint()
{
super.hint();
// buff.g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
}
public void setRadius(float r)
{
this.radius = r;
}
public void setRadii(float inner, float outer)
{
this.innerRadius = inner;
this.radius = outer;
layout();
}
public MenuItem create(String s)
{
/*
* Attempt to automatically find a good hint character to use with the
* new RadialMenuItem
*/
boolean foundGoodChar = false;
int charInd = 0;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (!alreadyContainsChar(c) && Character.isLetter(c))
{
foundGoodChar = true;
charInd = i;
break;
}
}
if (!foundGoodChar)
{
charInd = 0; // Oh well, let's just use the first char again.
}
if (s.length() == 0)
return create(s,' ');
return create(s, s.charAt(charInd));
}
protected boolean alreadyContainsChar(char c)
{
for (int i = 0; i < items.size(); i++)
{
RadialMenuItem rmi = (RadialMenuItem) items.get(i);
if (rmi.alreadyContainsChar(c))
return true;
}
return false;
}
public RadialMenuItem create(String s, char c)
{
RadialMenuItem rmi = new RadialMenuItem();
rmi.setName(s);
rmi.setHint(String.valueOf(c));
return rmi;
}
public void setArc(float thetaLo, float thetaHi)
{
this.thetaLo = thetaLo;
this.thetaHi = thetaHi;
layout();
}
public synchronized void layout()
{
if (items.size() == 0)
return;
float dTheta = thetaHi - thetaLo;
float thetaStep = dTheta / items.size();
float start = -PConstants.HALF_PI;
for (int i = 0; i < items.size(); i++)
{
RadialMenuItem seg = (RadialMenuItem) items.get(i);
seg.setPosition(x, y);
float curTheta = start + i * thetaStep;
seg.layout(innerRadius, radius, curTheta, curTheta + thetaStep);
}
findMaxLevelOpen();
}
public void findMaxLevelOpen()
{
MenuItem item = this;
maxLevelOpen = 0;
boolean shouldContinue = true;
while (shouldContinue)
{
maxLevelOpen++;
shouldContinue = false;
for (int i = 0; i < item.items.size(); i++)
{
MenuItem item2 = (MenuItem) item.items.get(i);
if (item2.isOpen())
{
item = item2;
shouldContinue = true;
break;
}
}
}
}
//
// public void draw()
// {
// super.draw();
// }
//
// protected void getRect(Rectangle2D.Float rect, Rectangle2D.Float buff)
// {
// super.getRect(rect, buff);
// }
public void setPosition(float x, float y)
{
this.x = x;
this.y = y;
layout();
}
public void itemMouseEvent(MouseEvent e, Point pt)
{
super.itemMouseEvent(e, pt);
myRect.setRect(x, y, 0, 0);
myRect.setRect(x, y, 0, 0);
getRect(myRect, buffRect);
float dist = myRect.distToPoint(pt);
float fadeDist = Math.max(myRect.width, myRect.height)
* FADE_DIST_MULTIPLIER;
fadeDist = Math.max(fadeDist, radius);
if (dist < fadeDist)
{
if (autoDim)
{
float normalized = 1f - (dist / fadeDist);
aTween.continueTo(normalized);
aTween.fforward();
}
} else
{
close();
}
}
public void keyEvent(KeyEvent e)
{
if (!isOpen())
return;
if (e.getID() == KeyEvent.KEY_TYPED)
{
ArrayList temp = (ArrayList) items.clone();
Collections.sort(temp, visComp);
for (int i = 0; i < temp.size(); i++)
{
if (!e.isConsumed())
{
RadialMenuItem item = (RadialMenuItem) temp.get(i);
item.keyHintEvent(e);
}
}
}
e.consume();
}
}