/*******************************************************************************
* 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.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.geom.Rectangle2D.Float;
import org.andrewberman.ui.Color;
import org.andrewberman.ui.FocusManager;
import org.andrewberman.ui.Point;
import org.andrewberman.ui.UIUtils;
import org.andrewberman.ui.ifaces.Positionable;
import org.andrewberman.ui.ifaces.Sizable;
import processing.core.PApplet;
import processing.core.PFont;
/**
* The <code>ToolbarItem</code> class is a MenuItem that belongs to a Toolbar
* object. It represents the "base" MenuItem of a Toolbar, i.e. the actual item
* that says "File", "Edit", and so on.
*
* @author Greg
*/
public class ToolbarItem extends MenuItem
{
static AffineTransform at = new AffineTransform();
static RoundRectangle2D.Float buffRoundRect = new RoundRectangle2D.Float(0,
0, 0, 0, 0, 0);
protected static final int LAYOUT_BELOW = 0;
protected static final int LAYOUT_LEFT = 2;
protected static final int LAYOUT_RIGHT = 1;
static RoundRectangle2D.Float roundRect = new RoundRectangle2D.Float(0, 0,
0, 0, 0, 0);
static final float shortcutTextSize = .75f;
static Area tri;
static float triWidth;
boolean drawChildrenTriangle;
protected int layoutMode;
Rectangle2D.Float subItemRect = new Rectangle2D.Float();
float tWidth, shortcutWidth;
public ToolbarItem()
{
super();
}
protected void calcPreferredSize()
{
super.calcPreferredSize();
PFont font = getFont();
float fs = getFontSize();
float px = getPadX();
float py = getPadY();
/*
* Calculate the text rectangle size.
*/
tWidth = UIUtils.getTextWidth(menu.canvas.g, font, fs, getName(), true);
/*
* For the height, let's use the height of some capital letters.
*/
float tHeight = UIUtils.getTextHeight(menu.canvas.g, font, fs, "XYZ",
true);
float triangleWidth = 0;
if (drawChildrenTriangle && items.size() > 0
&& layoutMode != LAYOUT_BELOW)
{
/*
* Calculate the width of the "submenu" triangle shape.
*/
at = AffineTransform.getScaleInstance(tHeight / 2f, tHeight / 2f);
Area tri = (Area) getStyle().get("subTriangle");
Area a = tri.createTransformedArea(at);
ToolbarItem.tri = a;
ToolbarItem.triWidth = (float) a.getBounds2D().getWidth();
triangleWidth = triWidth + px;
}
shortcutWidth = 0;
if (shortcut != null)
{
shortcutWidth = px
+ UIUtils.getTextWidth(menu.buff, font, fs
* shortcutTextSize, shortcut.label, true);
}
setWidth(tWidth + triangleWidth + shortcutWidth + 2 * px);
setHeight(tHeight + 2 * py);
}
protected boolean containsPoint(Point p)
{
float ro = getStyle().getF("f.roundOff");
buffRoundRect.setRoundRect(x, y, width, height, ro, ro);
return buffRoundRect.contains(p);
}
/**
* Normally, the MenuItem's create() method just defers back to the nearest
* Menu it can use to create an item, but here we want to change some
* options, so let's override it.
*/
public MenuItem create(String label)
{
ToolbarItem ti = new ToolbarItem();
ti.setLayoutMode(ToolbarItem.LAYOUT_RIGHT);
ti.drawChildrenTriangle = true;
ti.setName(label);
return ti;
}
protected void drawBefore()
{
if (isOpen() && items.size() > 0)
MenuUtils.drawBackgroundRoundRect(this, subItemRect.x,
subItemRect.y, subItemRect.width, subItemRect.height);
}
protected void drawMyself()
{
float ro = getStyle().getF("f.roundOff");
Color strokeC = getStrokeColor();
Stroke stroke = getStroke();
float px = getStyle().getF("f.padX");
float py = getStyle().getF("f.padY");
roundRect.setRoundRect(x, y, width, height, ro, ro);
Graphics2D g2 = menu.buff.g2;
/*
* Set the correct fill gradient
*/
// if (!isEnabled())
// {
// g2.setPaint(getStyle().getC("c.disabled"));
// } else
if (isOpen() && parent == menu)
{
g2.setPaint(getStyle().getGradient(MenuItem.DOWN, y, y + height));
} else if (!hasChildren() && getState() == MenuItem.DOWN)
{
g2.setPaint(getStyle().getGradient(MenuItem.DOWN, y, y + height));
} else
g2.setPaint(getStyle().getGradient(getState(), y, y + height));
/*
* Only perform the fill if the mood is right.
*/
// if (getState() != MenuItem.UP || isOpen())
// {
// if (!isEnabled())
// {
// ;
// } else if (menu.hovered != null && menu.hovered != this
// && !isAncestorOfHovered())
// {
// ;
// } else
// {
// g2.fill(roundRect);
// g2.setPaint(strokeC);
// g2.setStroke(stroke);
// g2.draw(roundRect);
// }
// }
if (shouldPerformFill())
{
g2.fill(roundRect);
g2.setPaint(strokeC);
g2.setStroke(stroke);
g2.draw(roundRect);
}
/*
* Draw the text, triangle, and shortcut.
*/
float curX = x + px;
MenuUtils.drawLeftText(this, getName(), curX);
curX += tWidth;
if (shortcut != null)
{
PFont font = getStyle().getFont("font");
float fs = getStyle().getF("f.fontSize");
float rightX = getX() + getWidth();
curX = rightX - shortcutWidth;
// curX += getStyle().padX;
float shortSize = fs * shortcutTextSize;
float descent = UIUtils.getTextDescent(menu.buff, font, shortSize,
true);
g2.setFont(font.getFont().deriveFont(shortSize));
g2.setPaint(strokeC.brighter(100));
float ht = UIUtils.getTextHeight(menu.canvas.g, font, shortSize,
shortcut.label, true);
float yOffset = (height - ht) / 2f + descent;
yOffset += ht / 2;
g2.drawString(shortcut.label, curX, y + yOffset);
}
curX += shortcutWidth;
if (drawChildrenTriangle && items.size() > 0)
{
if (layoutMode == LAYOUT_BELOW && getState() != MenuItem.UP
&& !isOpen())
{
curX = x + width / 2;
at.setToIdentity();
at.translate(curX, y + height + py / 2);
at.rotate(PApplet.HALF_PI);
Area a2 = tri.createTransformedArea(at);
g2.setPaint(strokeC);
g2.fill(a2);
} else if (layoutMode != LAYOUT_BELOW)
{
curX = x + width - triWidth - px;
at.setToIdentity();
at.translate(curX, y + height / 2);
Area a2 = tri.createTransformedArea(at);
g2.setPaint(strokeC);
g2.fill(a2);
}
}
}
protected void getRect(Rectangle2D.Float rect, Rectangle2D.Float buff)
{
buff.setFrame(x, y, width, height);
Rectangle2D.union(rect, buff, rect);
super.getRect(rect, buff);
}
protected void itemMouseEvent(MouseEvent e, Point pt)
{
/*
* I'm doing this actionOnMouseDown stuff so that the top-level menus
* are activated on a mouse press, to be more toolbar-like (I'm looking
* to match Eclipse-like functionality). Basically, I'm overriding the
* default values if we're in a top-level menu.
*/
super.itemMouseEvent(e, pt);
// if (parent == menu && mouseInside)
// {
// if (e.getID() == MouseEvent.MOUSE_RELEASED)
// {
// if (!isOpen())
// {
// menuTriggerLogic();
// } else
// {
// close();
// }
// }
// // System.out.println("Hey!");
// }
}
// protected void setState(int state)
// {
// super.setState(state);
// if (this.state == state)
// return;
// if (menu == parent && menu instanceof Toolbar)
// {
// boolean oldHov = menu.hoverNavigable;
// menu.hoverNavigable = false;
// super.setState(state);
// Toolbar tb = (Toolbar) menu;
// if (state != MenuItem.UP)
// {
// if (tb.isActive())
// {
// tb.closeMyChildren();
// open();
// }
// }
// menu.hoverNavigable = oldHov;
// } else
// super.setState(state);
// }
public synchronized void layout()
{
if (menu == null)
return;
float px = getPadX();
float py = getPadY();
float curX = 0, curY = 0;
switch (layoutMode)
{
case (LAYOUT_BELOW):
curX = x - py;
curY = y + height;
break;
case (LAYOUT_RIGHT):
default:
curX = x + width;
curY = y - px;
break;
}
subItemRect.x = curX;
subItemRect.y = curY;
curX += px;
curY += py;
for (int i = 0; i < items.size(); i++)
{
MenuItem item = (MenuItem) items.get(i);
item.calcPreferredSize();
}
float maxWidth = getMaxWidth();
float maxHeight = getMaxHeight();
for (int i = 0; i < items.size(); i++)
{
MenuItem item = (MenuItem) items.get(i);
if (item.isHidden())
continue;
item.setPosition(curX, curY);
// if (layoutMode == LAYOUT_BELOW)
// item.setWidth(maxWidth);
// else
// item.setHeight(maxHeight);
item.setSize(maxWidth, maxHeight);
curY += item.getHeight();
}
curY += py;
subItemRect.width = maxWidth + px * 2;
subItemRect.height = curY - subItemRect.y;
/*
* Trigger the recursive layout.
*/
super.layout();
}
protected void setLayoutMode(int layoutMode)
{
this.layoutMode = layoutMode;
}
}