/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program 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.
*/
package org.geogebra.desktop.gui.toolbar;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.JToolTip;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.ModeSetter;
import org.geogebra.common.main.App;
import org.geogebra.desktop.awt.GGraphics2DD;
import org.geogebra.desktop.javax.swing.GPopupMenuD;
import org.geogebra.desktop.main.AppD;
/**
* JToggle button combined with popup menu for mode selection
*/
public class ModeToggleMenuD extends JPanel {
private static final long serialVersionUID = 1L;
ModeToggleButtonGroup bg;
private MyJToggleButton tbutton, mouseOverButton;
private JPopupMenu popMenu;
private ArrayList<JMenuItem> menuItemList;
private ActionListener popupMenuItemListener;
private AppD app;
int size;
private ToolbarD toolbar;
final static Color bgColor = Color.white;
public ModeToggleMenuD(AppD app, ToolbarD toolbar,
ModeToggleButtonGroup bg) {
this.app = app;
this.bg = bg;
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
this.toolbar = toolbar;
tbutton = new MyJToggleButton(this, toolbar, app);
tbutton.setAlignmentY(BOTTOM_ALIGNMENT);
add(tbutton);
popMenu = new JPopupMenu();
popMenu.setBackground(bgColor);
menuItemList = new ArrayList<JMenuItem>();
popupMenuItemListener = new MenuItemListener();
size = 0;
}
public int getToolsCount() {
return size;
}
public JToggleButton getJToggleButton() {
return tbutton;
}
public boolean selectMode(int mode) {
String modeText = mode + "";
for (int i = 0; i < size; i++) {
JMenuItem mi = menuItemList.get(i);
// found item for mode?
if (mi.getActionCommand().equals(modeText)) {
selectItem(mi, ModeSetter.DOCK_PANEL);
return true;
}
}
return false;
}
public int getFirstMode() {
if (menuItemList == null || menuItemList.size() == 0) {
return -1;
}
JMenuItem mi = menuItemList.get(0);
return Integer.parseInt(mi.getActionCommand());
}
private void selectItem(JMenuItem mi, ModeSetter ms) {
// check if the menu item is already selected
boolean imageDialog = mi.getActionCommand()
.equals(Integer.toString(EuclidianConstants.MODE_IMAGE));
if (tbutton.isSelected()
&& tbutton.getActionCommand().equals(mi.getActionCommand())
&& !imageDialog) {
return;
}
tbutton.setIcon(mi.getIcon());
tbutton.setToolTipText(app
.getToolTooltipHTML(Integer.parseInt(mi.getActionCommand())));
tbutton.setActionCommand(mi.getActionCommand());
tbutton.setSelected(true);
if (imageDialog && ms == ModeSetter.TOOLBAR) {
tbutton.doClick();
}
// tbutton.requestFocus();
}
public void addMode(int mode) {
// add menu item to popup menu
JMenuItem mi = new JMenuItem();
mi.setFont(app.getPlainFont());
mi.setBackground(bgColor);
// tool name as text
mi.setText(app.getToolName(mode));
Icon icon = app.getModeIcon(mode);
String actionText = Integer.toString(mode);
mi.setIcon(icon);
mi.setActionCommand(actionText);
mi.addActionListener(popupMenuItemListener);
popMenu.add(mi);
menuItemList.add(mi);
size++;
if (size == 1) {
// init tbutton
tbutton.setIcon(icon);
tbutton.setActionCommand(actionText);
// tooltip: tool name and tool help
tbutton.setToolTipText(app.getToolTooltipHTML(mode));
// add button to button group
bg.add(tbutton);
}
app.setComponentOrientation(mi);
}
/**
* Removes all modes from the toggle menu. Used for the temporary
* perspective.
*
* @author Florian Sonner
* @version 2008-10-22
*/
public void clearModes() {
popMenu.removeAll();
menuItemList.clear();
size = 0;
}
public void addSeparator() {
popMenu.addSeparator();
}
// sets new mode when item in popup menu is selected
private class MenuItemListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem item = (JMenuItem) e.getSource();
if (!(Integer.toString(EuclidianConstants.MODE_IMAGE)
.equals(item.getActionCommand()))) {
selectItem(item, ModeSetter.TOOLBAR);
tbutton.doClick();
} else {
tbutton.setSelected(false);
tbutton.getModel().setRollover(false);
tbutton.repaint();
String oldCmd = tbutton.getActionCommand();
tbutton.setActionCommand(item.getActionCommand());
tbutton.doClick();
tbutton.setActionCommand(oldCmd);
}
}
}
public void setMouseOverButton(MyJToggleButton bt) {
mouseOverButton = bt;
repaint();
}
public JToggleButton getMouseOverButton() {
return mouseOverButton;
}
public void mouseOver() {
// popup menu is showing
JPopupMenu activeMenu = GPopupMenuD.getImpl(bg.getActivePopupMenu());
if (activeMenu != null && activeMenu.isShowing()) {
setPopupVisible(true);
}
}
// shows popup menu
public void setPopupVisible(boolean flag) {
if (flag) {
bg.setActivePopupMenu(new GPopupMenuD(popMenu));
if (popMenu.isShowing()) {
return;
}
Point locButton = tbutton.getLocationOnScreen();
Component component = SwingUtilities.getRootPane(tbutton);
if (component == null)
{
component = app.getMainComponent(); // if geogebrapanel is
}
// inside an awt window
Point locApp = component.getLocationOnScreen();
if (toolbar.getOrientation() == SwingConstants.HORIZONTAL) {
tbutton.repaint();
int offsetx = 0;
if (app.getLocalization().isRightToLeftReadingOrder()) {
// needed otherwise popMenu.getWidth() can return 0
popMenu.setVisible(true);
offsetx = popMenu.getWidth() - tbutton.getWidth();
}
popMenu.show(component, locButton.x - locApp.x - offsetx,
locButton.y - locApp.y + tbutton.getHeight());
} else {
popMenu.show(component,
locButton.x - locApp.x + tbutton.getWidth(),
locButton.y - locApp.y + tbutton.getHeight() / 2);
}
} else {
popMenu.setVisible(false);
}
tbutton.repaint();
}
public boolean isPopupShowing() {
return popMenu.isShowing();
}
public void setMode(int mode) {
app.setMode(mode);
}
}
class MyJToggleButton extends JToggleButton
implements MouseListener, MouseMotionListener, ActionListener {
private static final long serialVersionUID = 1L;
private int BORDER = 6;
private int iconWidth, iconHeight;
private GeneralPath gp;
private boolean showToolTipText = true;
boolean popupTriangleHighlighting = false;
boolean popupTriangleClicked = false;
ModeToggleMenuD menu;
private static final Color arrowColor = new Color(0, 0, 0, 130);
// private static final Color selColor = new Color(166, 11, 30,150);
// private static final Color selColor = new Color(17, 26, 100, 200);
private static final Color selColor = new Color(0, 0, 153, 200);
private static final BasicStroke selStroke = new BasicStroke(3f);
private Timer showMenuTimer;
private ToolbarD toolbar;
private App app;
MyJToggleButton(ModeToggleMenuD menu, ToolbarD toolbar, App app) {
super();
this.menu = menu;
this.toolbar = toolbar;
this.app = app;
// add own listeners
addMouseListener(this);
addMouseMotionListener(this);
addActionListener(this);
}
@Override
public String getToolTipText() {
if (showToolTipText) {
return super.getToolTipText();
}
return null;
}
// set mode
@Override
public void actionPerformed(ActionEvent e) {
// don't select mode if popup triangle clicked
if (!popupTriangleClicked || !menu.isPopupShowing()) {
menu.setMode(Integer.parseInt(e.getActionCommand()));
}
}
@Override
public void setIcon(Icon icon) {
super.setIcon(icon);
iconWidth = icon.getIconWidth();
iconHeight = icon.getIconHeight();
BORDER = (int) Math.round(icon.getIconWidth() * 0.1875); // 6 pixel
// border
// for 32
// pixel
// icon
Dimension dim = new Dimension(iconWidth + 2 * BORDER,
iconHeight + 2 * BORDER);
setPreferredSize(dim);
setMinimumSize(dim);
setMaximumSize(dim);
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Stroke oldStroke = g2.getStroke();
super.paint(g2);
if (isSelected()) {
g2.setColor(selColor);
g2.setStroke(selStroke);
g2.drawRect(BORDER - 1, BORDER - 1, iconWidth + 1, iconHeight + 1);
g2.setStroke(oldStroke);
}
// draw little arrow (for popup menu)
if (menu.size > 1) {
if (gp == null) {
initPath();
}
GGraphics2DD.setAntialiasing(g2);
if (menu.getMouseOverButton() == this
&& (popupTriangleHighlighting || menu.isPopupShowing())) {
int x = BORDER + iconWidth + 2;
int y = BORDER + iconHeight + 1;
// background glow circle
g2.setColor(Color.LIGHT_GRAY);
if (iconWidth <= 32) {
g2.fillOval(x - 9, y - 9, 12, 12);
} else if (iconWidth <= 40) {
g2.fillOval(x - 10, y - 9, 13, 13);
} else if (iconWidth <= 50) {
g2.fillOval(x - 11, y - 9, 14, 14);
} else {
g2.fillOval(x - 12, y - 12, 15, 15);
}
g2.setColor(Color.red);
g2.fill(gp);
g2.setColor(Color.black);
g2.draw(gp);
} else {
g2.setColor(Color.white);
g2.fill(gp);
g2.setColor(arrowColor);
g2.draw(gp);
}
}
}
private void initPath() {
gp = new GeneralPath();
int x = BORDER + iconWidth + 2;
int y = BORDER + iconHeight + 1;
if (iconWidth <= 40) {
gp.moveTo(x - 6, y - 5);
gp.lineTo(x, y - 5);
gp.lineTo(x - 3, y);
} else {
gp.moveTo(x - 8, y - 7);
gp.lineTo(x, y - 7);
gp.lineTo(x - 4, y);
}
/*
* if (iconWidth > 32) { gp.moveTo(x - 8, y - 7); gp.lineTo(x, y - 7);
* gp.lineTo(x - 4, y); } else if (iconWidth > 20) { gp.moveTo(x - 6, y
* - 5); gp.lineTo(x, y - 5); gp.lineTo(x - 3, y); } else { gp.moveTo(x
* - 4, y - 3); gp.lineTo(x, y - 3); gp.lineTo(x - 2, y); }
*/
gp.closePath();
}
private boolean popupTriangleClicked(int x, int y) {
popupTriangleClicked = (menu.size > 1 && y > iconHeight - 4);
return popupTriangleClicked;
}
@Override
public void mouseClicked(MouseEvent e) {
if (!menu.isPopupShowing() && e.getClickCount() == 2) {
menu.setPopupVisible(true);
}
}
int defaultInitialDelay;
@Override
public void mouseEntered(MouseEvent arg0) {
defaultInitialDelay = ToolTipManager.sharedInstance().getInitialDelay();
if (toolbar.preventToolTipDelay()) {
ToolTipManager.sharedInstance().setInitialDelay(0);
}
menu.setMouseOverButton(this);
}
@Override
public void mousePressed(MouseEvent e) {
if (!menu.isPopupShowing()
&& popupTriangleClicked(e.getX(), e.getY())) {
menu.setPopupVisible(true);
this.getModel().setArmed(false);
}
else {
// Display the menu after a specific amount of time as well, start
// a timer for this. actionPerformed method stops this timer,
// which ensures that the menu is displayed after user released the
// mouse
if (showMenuTimer == null) {
showMenuTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e1) {
menu.setPopupVisible(true);
showMenuTimer.stop();
}
});
showMenuTimer.setRepeats(false);
}
showMenuTimer.start();
}
}
@Override
public void mouseExited(MouseEvent arg0) {
menu.setMouseOverButton(null);
if (popupTriangleHighlighting) {
popupTriangleHighlighting = false;
repaint();
}
// Stop the timer to show the menu if the user is no longer
// hovering over this button.
if (showMenuTimer != null && showMenuTimer.isRunning()) {
showMenuTimer.stop();
}
ToolTipManager.sharedInstance().setInitialDelay(defaultInitialDelay);
}
@Override
public void mouseReleased(MouseEvent e) {
// nothing to do
}
@Override
public void doClick() {
super.doClick();
if (!hasFocus()) {
requestFocusInWindow();
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (popupTriangleClicked(e.getX(), e.getY())) {
menu.setPopupVisible(true);
}
}
@Override
public void mouseMoved(MouseEvent e) {
menu.mouseOver();
showToolTipText = !menu.isPopupShowing();
// highlight popup menu triangle
if (menu.size > 1
&& popupTriangleHighlighting != popupTriangleClicked(e.getX(),
e.getY())) {
popupTriangleHighlighting = !popupTriangleHighlighting;
repaint();
}
}
private JToolTip tip;
@Override
public JToolTip createToolTip() {
tip = super.createToolTip();
tip.setBorder(BorderFactory.createCompoundBorder(tip.getBorder(),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));
return tip;
}
@Override
public Point getToolTipLocation(MouseEvent event) {
Point p = new Point();
switch (app.getToolbarPosition()) {
case SwingConstants.NORTH:
p.y = this.getY() + this.getHeight();
p.x = this.getX();
break;
default:
case SwingConstants.SOUTH:
p.y = this.getY();
p.x = this.getX();
if (tip != null) {
p.y -= tip.getHeight();
} else {
p.y += this.getHeight();
}
break;
case SwingConstants.WEST:
p.y = this.getY();
p.x = this.getX() + this.getWidth();
break;
case SwingConstants.EAST:
p.y = this.getY();
p.x = this.getX();
if (tip != null) {
p.x -= tip.getWidth();
} else {
p.x += this.getWidth();
}
break;
}
return p;
}
}