/*
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.view.consprotocol;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.NumberEditor;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.geogebra.common.gui.SetLabels;
import org.geogebra.common.gui.view.consprotocol.ConstructionProtocolNavigation;
import org.geogebra.common.gui.view.consprotocol.ConstructionProtocolView;
import org.geogebra.common.main.App;
import org.geogebra.common.main.settings.AbstractSettings;
import org.geogebra.common.main.settings.ConstructionProtocolSettings;
import org.geogebra.common.main.settings.SettingListener;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.menubar.GeoGebraMenuBar;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.main.LocalizationD;
import org.geogebra.desktop.util.GuiResourcesD;
/**
* Navigation buttons for the construction protocol
*/
public class ConstructionProtocolNavigationD
extends ConstructionProtocolNavigation
implements ActionListener, SettingListener, SetLabels {
private JButton btFirst, btPrev, btNext, btLast, btOpenWindow;
/** Button for starting/stopping animation */
JButton btPlay;
private JLabel lbSteps;
/** Delay spinner */
JSpinner spDelay;
/** Application */
AppD app;
/** Construction protocol view */
ConstructionProtocolViewD prot;
private AutomaticPlayer player;
/**
* ConstructionProtocolNavigation panel
*/
private JPanel implPanel;
private LocalizationD loc;
private JPanel playPanel;
/**
* Creates a new navigation bar to step through the construction protocol.
*
* @param app
* application
*/
public ConstructionProtocolNavigationD(AppD app, int viewID) {
implPanel = new JPanel();
this.app = app;
this.loc = app.getLocalization();
this.setViewID(viewID);
SpinnerModel model = new SpinnerNumberModel(2, // initial value
0.25, // min
10, // max
0.25); // step
spDelay = new JSpinner(model);
NumberEditor numEdit = new JSpinner.NumberEditor(spDelay, "#.##");
DecimalFormat format = numEdit.getFormat();
format.setDecimalFormatSymbols(
new DecimalFormatSymbols(Locale.ENGLISH));
lbSteps = new JLabel();
// done when needed, later
// initGUI();
/*
* //next 3 rows moved into EuclidianDockPanel.loadComponent //because
* it not neccessary for all Contruction protocol navigation issue
* ConstructionProtocolSettings cps =
* app.getSettings().getConstructionProtocol(); settingsChanged(cps);
* cps.addListener(this);
*/
}
/**
* @return underlying JPanel implementation
*/
public JPanel getImpl() {
return implPanel;
}
/**
* @param flag
* whether button to show construction protocol should be visible
*/
@Override
public void setConsProtButtonVisible(boolean flag) {
showConsProtButton = flag;
if (btOpenWindow != null) {
btOpenWindow.setVisible(isConsProtButtonVisible());
}
}
/**
* Changes animation delay
*
* @param delay
* delay in seconds
*/
@Override
public void setPlayDelay(double delay) {
playDelay = delay;
try {
spDelay.setValue(Double.valueOf(playDelay));
} catch (Exception e) {
spDelay.setValue(Integer.valueOf((int) Math.round(playDelay)));
}
}
/**
* Initializes all components, sets labels
*/
public void initGUI() {
implPanel.removeAll();
btFirst = new JButton(app.getScaledIcon(GuiResourcesD.NAV_SKIPBACK64));
btLast = new JButton(
app.getScaledIcon(GuiResourcesD.NAV_SKIPFORWARD64));
btPrev = new JButton(app.getScaledIcon(GuiResourcesD.NAV_REWIND64));
btNext = new JButton(
app.getScaledIcon(GuiResourcesD.NAV_FASTFORWARD64));
btFirst.addActionListener(this);
btLast.addActionListener(this);
btPrev.addActionListener(this);
btNext.addActionListener(this);
JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
leftPanel.add(btFirst);
leftPanel.add(btPrev);
leftPanel.add(lbSteps);
leftPanel.add(btNext);
leftPanel.add(btLast);
playPanel = new JPanel();
playPanel.setVisible(showPlayButton);
playPanel.add(Box.createRigidArea(new Dimension(20, 10)));
btPlay = new JButton();
btPlay.setIcon(new ImageIcon(app.getPlayImage()));
btPlay.addActionListener(this);
spDelay.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
try {
playDelay = Double
.parseDouble(spDelay.getValue().toString());
} catch (Exception ex) {
playDelay = 2;
}
}
});
playPanel.add(btPlay);
playPanel.add(spDelay);
playPanel.add(new JLabel("s"));
btOpenWindow = new JButton();
btOpenWindow.setIcon(app
.getScaledIcon(GuiResourcesD.MENU_VIEW_CONSTRUCTION_PROTOCOL));
btOpenWindow.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// app.getGuiManager().showConstructionProtocol();
if (!app.getGuiManager()
.showView(App.VIEW_CONSTRUCTION_PROTOCOL)) {
app.getGuiManager().setShowView(true,
App.VIEW_CONSTRUCTION_PROTOCOL);
}
// Checkbox of Construction protocol view will be checked in
// view menu
((GeoGebraMenuBar) (((GuiManagerD) app.getGuiManager())
.getMenuBar())).updateCPView(true);
}
});
btOpenWindow.setVisible(isConsProtButtonVisible());
// add panels together to center
implPanel.setLayout(new BoxLayout(this.implPanel, BoxLayout.LINE_AXIS));
implPanel.add(leftPanel);
implPanel.add(playPanel);
implPanel.add(btOpenWindow);
implPanel.add(Box.createRigidArea(new Dimension(20, 10)));
setLabels();
setPlayDelay(playDelay);
update();
}
@Override
public void setLabels() {
if (btPlay != null) {
btPlay.setText(loc.getPlain("Play"));
}
if (btOpenWindow != null) {
btOpenWindow.setToolTipText(
loc.getPlainTooltip("ConstructionProtocol"));
}
}
/**
* Updates the texts that show the current construction step and the number
* of construction steps.
*/
@Override
public void update() {
if (prot != null) {
int currentStep = prot.getCurrentStepNumber();
int stepNumber = prot.getLastStepNumber();
lbSteps.setText(currentStep + " / " + stepNumber);
}
}
@Override
public void register(ConstructionProtocolView constructionProtocolView) {
if (prot == null) {
initGUI();
}
if (constructionProtocolView instanceof ConstructionProtocolViewD) {
prot = (ConstructionProtocolViewD) constructionProtocolView;
prot.registerNavigationBar(this);
}
}
/**
* Unregisters this navigation bar from its protocol.
*/
public void unregister() {
prot.unregisterNavigationBar(this);
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
implPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
if (source == btFirst) {
prot.firstStep();
} else if (source == btLast) {
prot.lastStep();
} else if (source == btPrev) {
prot.previousStep();
} else if (source == btNext) {
prot.nextStep();
} else if (source == btPlay) {
if (isPlaying()) {
player.stopAnimation();
} else {
player = new AutomaticPlayer(playDelay);
player.startAnimation();
}
}
if (prot.getCpPanel().isVisible()) {
prot.scrollToConstructionStep();
}
implPanel.setCursor(Cursor.getDefaultCursor());
}
/**
* Make all components enabled / disabled
*
* @param flag
* whether components should be enabled
*/
void setComponentsEnabled(boolean flag) {
Component comps[] = implPanel.getComponents();
for (int i = 0; i < comps.length; i++) {
comps[i].setEnabled(flag);
}
btPlay.setEnabled(true);
lbSteps.setEnabled(true);
}
@Override
public void setButtonPlay() {
btPlay.setIcon(new ImageIcon(app.getPlayImage()));
btPlay.setText(loc.getPlain("Play"));
}
@Override
public void setButtonPause() {
btPlay.setIcon(new ImageIcon(app.getPauseImage()));
btPlay.setText(loc.getPlain("Pause"));
}
/**
* Steps through the construction automatically.
*/
private class AutomaticPlayer implements ActionListener {
private Timer timer; // for animation
/**
* Creates a new player to step through the construction automatically.
*
* @param delay
* in seconds between steps
*/
public AutomaticPlayer(double delay) {
timer = new Timer((int) (delay * 1000), this);
}
public synchronized void startAnimation() {
// dispatch events to play button
app.startDispatchingEventsTo(btPlay);
setPlaying(true);
app.setNavBarButtonPause();
setComponentsEnabled(false);
app.setWaitCursor();
if (prot.getCurrentStepNumber() == prot.getLastStepNumber()) {
prot.firstStep();
}
timer.start();
}
public synchronized void stopAnimation() {
timer.stop();
// unblock application events
app.stopDispatchingEvents();
setPlaying(false);
app.setNavBarButtonPlay();
setComponentsEnabled(true);
app.setDefaultCursor();
}
@Override
public synchronized void actionPerformed(ActionEvent e) {
prot.nextStep();
if (prot.getCurrentStepNumber() == prot.getLastStepNumber()) {
stopAnimation();
}
}
}
@Override
public void settingsChanged(AbstractSettings settings) {
ConstructionProtocolSettings cps = (ConstructionProtocolSettings) settings;
setPlayButtonVisible(cps.showPlayButton());
setPlayDelay(cps.getPlayDelay());
setConsProtButtonVisible(cps.showConsProtButton());
update();
}
@Override
public void setVisible(boolean visible) {
getImpl().setVisible(visible);
}
public void updateIcons() {
if (btFirst == null) {
return;
}
btFirst.setIcon(app.getScaledIcon(GuiResourcesD.NAV_SKIPBACK64));
btLast.setIcon(app.getScaledIcon(GuiResourcesD.NAV_SKIPFORWARD64));
btPrev.setIcon(app.getScaledIcon(GuiResourcesD.NAV_REWIND64));
btNext.setIcon(app.getScaledIcon(GuiResourcesD.NAV_FASTFORWARD64));
btOpenWindow.setIcon(app
.getScaledIcon(GuiResourcesD.MENU_VIEW_CONSTRUCTION_PROTOCOL));
lbSteps.setFont(app.getPlainFont());
update();
}
@Override
protected void setPlayPanelVisible(boolean flag) {
if (playPanel != null) {
this.playPanel.setVisible(flag);
}
}
}