package edu.kit.pse.ws2013.routekit.views;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.AbstractButton;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import edu.kit.pse.ws2013.routekit.controllers.MainController;
import edu.kit.pse.ws2013.routekit.controllers.ProfileMapManager;
import edu.kit.pse.ws2013.routekit.map.Graph;
import edu.kit.pse.ws2013.routekit.map.StreetMap;
import edu.kit.pse.ws2013.routekit.models.CurrentCombinationListener;
import edu.kit.pse.ws2013.routekit.models.ProfileMapCombination;
import edu.kit.pse.ws2013.routekit.models.RouteModel;
import edu.kit.pse.ws2013.routekit.models.RouteModelListener;
import edu.kit.pse.ws2013.routekit.profiles.Profile;
import edu.kit.pse.ws2013.routekit.routecalculation.RouteDescription;
import edu.kit.pse.ws2013.routekit.routecalculation.TurnInstruction;
import edu.kit.pse.ws2013.routekit.util.Coordinates;
/**
* Displays the main window on the screen.
*/
public class MainView extends JFrame implements RouteModelListener {
private static final long serialVersionUID = 1L;
final float maxcoordinate = Float.MAX_VALUE;
private JTextField startField;
private JTextField targetField;
private JLabel mapLabel;
private JLabel profileLabel;
private JFileChooser fileChooser = new JFileChooser();
private RouteModel routeModel;
private MapView mapView;
private JList<String> routeDescription;
private JButton historyButton;
private JButton swapButton;
private JMenuItem history;
private JMenuItem html;
private JMenuItem gpx;
private JScrollPane routeScroll;
/**
* A constructor that creates a new MainView.
*/
public MainView(RouteModel routeModel) {
super("routeKIT");
this.routeModel = routeModel;
routeModel.addRouteListener(this);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setSize(1000, 700);
initMenu();
JSplitPane contentPane = new JSplitPane();
JPanel left = initLeftPane(routeModel);
contentPane.add(left, JSplitPane.LEFT);
JPanel right = initRightPanel(routeModel);
contentPane.add(right, JSplitPane.RIGHT);
ProfileMapManager.getInstance().addCurrentCombinationListener(
new CurrentCombinationListener() {
@Override
public void currentCombinationChanged(
ProfileMapCombination newCombination) {
String oldMap = mapLabel.getText();
setCurrentProfile(newCombination.getProfile());
setCurrentMap(newCombination.getStreetMap());
mapView.setEnabled(newCombination.isCalculated());
mapView.setTileSource(MainController.getInstance()
.getTileSource());
startField.setText("");
targetField.setText("");
if (!mapLabel.getText().equals(oldMap)) {
center(newCombination);
}
if (!newCombination.isCalculated()) {
enableButtons(false);
textMessage("Für diese Kombination aus Karte und"
+ " Profil existiert keine Vorberechnung. Sie können "
+ "entweder jetzt eine Vorberechnung starten (Roter Knopf) "
+ "oder ein anderes Profil bzw. eine andere Karte auswählen.");
} else {
enableButtons(true);
if (targetField.getBackground() == Color.RED
|| startField.getBackground() == Color.RED) {
textMessage("Falsche Koordinaten wurden eingegeben."
+ " Bitte halten Sie sich an das Format: \"Breitengrad Längengrad\","
+ " die jeweils der Form [+-]?[0-9]+.[0-9]* sein sollen.");
} else {
textMessage("");
}
}
}
});
setContentPane(contentPane);
html.setEnabled(false);
gpx.setEnabled(false);
if (!ProfileMapManager.getInstance().getCurrentCombination()
.isCalculated()) {
mapView.setEnabled(false);
enableButtons(false);
textMessage("Für diese Kombination aus Karte und"
+ " Profil existiert keine Vorberechnung. Sie können "
+ "entweder jetzt eine Vorberechnung starten (Roter Knopf) "
+ "oder ein anderes Profil bzw. eine andere Karte auswählen.");
}
}
private void center(ProfileMapCombination Combination) {
Graph graph = Combination.getStreetMap().getGraph();
Coordinates firstNode = graph.getCoordinates(0);
Coordinates lastNode = graph
.getCoordinates(graph.getNumberOfNodes() - 1);
mapView.setMapLocation(firstNode, lastNode);
}
private void enableButtons(boolean value) {
startField.setEnabled(value);
targetField.setEnabled(value);
historyButton.setEnabled(value);
swapButton.setEnabled(value);
history.setEnabled(value);
html.setEnabled(value);
gpx.setEnabled(value);
}
private JPanel initLeftPane(final RouteModel routeModel) {
JPanel left = new JPanel(new BorderLayout());
left.setMinimumSize(new Dimension(260, 100));
JPanel controls = new JPanel(new BorderLayout());
JPanel hist = new JPanel();
historyButton = new JButton("Verlauf");
hist.add(historyButton);
historyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new HistoryView(MainController.getInstance().getHistory(),
MainView.this, mapView);
}
});
controls.add(hist, BorderLayout.SOUTH);
JPanel swap = new JPanel();
swapButton = new JButton(new ImageIcon(
MainView.class.getResource("swap.png")));
swapButton.setMargin(new Insets(0, 0, 0, 0));
swap.add(swapButton);
controls.add(swap, BorderLayout.WEST);
controls.add(initCoordEntry(), BorderLayout.CENTER);
swapButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String starttemp = startField.getText();
startField.setText(targetField.getText());
targetField.setText(starttemp);
if (startField.getText().equals("")
&& targetField.getText().equals("")) {
startField.setBackground(Color.WHITE);
targetField.setBackground(Color.WHITE);
textMessage("");
}
if (!startField.getBackground().equals(
targetField.getBackground())) {
Color colortemp = startField.getBackground();
startField.setBackground(targetField.getBackground());
targetField.setBackground(colortemp);
}
if (startField.getBackground().equals(Color.WHITE)
&& targetField.getBackground().equals(Color.WHITE)
&& !startField.getText().equals("")
&& !targetField.getText().equals("")) {
MainController.getInstance().setStartAndDestinationPoint(
routeModel.getDestination(), routeModel.getStart());
}
}
});
left.add(controls, BorderLayout.NORTH);
routeDescription = new JList<String>(new DefaultListModel<String>());
routeScroll = new JScrollPane(routeDescription);
left.add(routeScroll, BorderLayout.CENTER);
routeDescription.setCellRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, false,
false);
return this;
}
});
return left;
}
private JPanel initCoordEntry() {
JPanel coords = new JPanel(new GridLayout(2, 1));
JPanel start = new JPanel();
JPanel target = new JPanel();
JLabel startLabel = new JLabel("Start");
JLabel targetLabel = new JLabel("Ziel");
Dimension startD = startLabel.getPreferredSize();
Dimension targetD = targetLabel.getPreferredSize();
Dimension preffered = new Dimension((int) Math.max(startD.getWidth(),
targetD.getWidth()), (int) Math.max(startD.getHeight(),
targetD.getHeight()));
startLabel.setPreferredSize(preffered);
targetLabel.setPreferredSize(preffered);
start.add(startLabel);
startField = new JTextField(15);
startField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
callController(startField);
}
});
startField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
callController(startField);
}
@Override
public void focusGained(FocusEvent e) {
startField.setBackground(Color.WHITE);
if (targetField.getBackground() == Color.WHITE) {
textMessage("");
}
}
});
start.add(startField);
target.add(targetLabel);
targetField = new JTextField(15);
target.add(targetField);
targetField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
callController(targetField);
}
});
targetField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
callController(targetField);
}
@Override
public void focusGained(FocusEvent e) {
targetField.setBackground(Color.WHITE);
if (startField.getBackground() == Color.WHITE) {
textMessage("");
}
}
});
coords.add(start);
coords.add(target);
return coords;
}
private void callController(JTextField textfield) {
Coordinates coordinates = null;
try {
coordinates = Coordinates.fromString(textfield.getText());
// can throw IllegalArgumentException
} catch (IllegalArgumentException e) {
textfield.setBackground(Color.RED);
textMessage("Falsche Koordinaten wurden eingegeben."
+ " Bitte halten Sie sich an das Format: \"Breitengrad Längengrad\","
+ " die jeweils der Form [+-]?[0-9]+.[0-9]* sein sollen.");
}
if (textfield == startField) {
MainController.getInstance().setStartPoint(coordinates);
} else {
MainController.getInstance().setDestinationPoint(coordinates);
}
}
private JPanel initRightPanel(RouteModel rm) {
JPanel right = new JPanel();
JPanel buttons = new JPanel();
JButton mapButton = new JButton("Karte");
mapButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MainController.getInstance().manageMaps();
}
});
buttons.add(mapButton);
ProfileMapCombination currentCombination = ProfileMapManager
.getInstance().getCurrentCombination();
mapLabel = new JLabel(currentCombination.getStreetMap().getName());
buttons.add(mapLabel);
JButton profileButton = new JButton("Profil");
profileButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MainController.getInstance().manageProfiles();
}
});
buttons.add(profileButton);
profileLabel = new JLabel(currentCombination.getProfile().getName());
buttons.add(profileLabel);
buttons.setBackground(Color.WHITE);
right.setLayout(new BorderLayout());
right.add(buttons, BorderLayout.NORTH);
mapView = new MapView(MainController.getInstance().getTileSource(), rm);
center(ProfileMapManager.getInstance().getCurrentCombination());
right.add(mapView, BorderLayout.CENTER);
return right;
}
private void updateRenderer() {
mapView.setTileSource(MainController.getInstance().getTileSource());
}
private void initMenu() {
JMenuBar menu = new JMenuBar();
JMenu routeKIT = new JMenu("routeKIT");
history = new JMenuItem("Verlauf...");
history.setMnemonic('v');
history.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new HistoryView(MainController.getInstance().getHistory(),
MainView.this, mapView);
}
});
JMenuItem about = new JMenuItem("Über...");
about.setMnemonic('e');
about.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new AboutView(MainView.this);
}
});
JMenuItem exit = new JMenuItem("Beenden");
exit.setMnemonic('b');
exit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
routeKIT.add(history);
routeKIT.add(about);
routeKIT.add(exit);
JMenu export = new JMenu("Export");
html = new JMenuItem("HTML...");
html.setMnemonic('h');
html.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = fileChooser.showDialog(MainView.this,
"Exportieren");
if (result == JFileChooser.APPROVE_OPTION
&& fileChooser.getSelectedFile() != null) {
MainController.getInstance().exportHTML(
fileChooser.getSelectedFile());
}
}
});
gpx = new JMenuItem("GPX...");
gpx.setMnemonic('g');
gpx.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = fileChooser.showDialog(MainView.this,
"Exportieren");
if (result == JFileChooser.APPROVE_OPTION
&& fileChooser.getSelectedFile() != null) {
MainController.getInstance().exportGPX(
fileChooser.getSelectedFile());
}
}
});
export.add(html);
export.add(gpx);
JMenu admin = new JMenu("Verwaltung");
JMenuItem profile = new JMenuItem("Profil...");
profile.setMnemonic('p');
profile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MainController.getInstance().manageProfiles();
}
});
JMenuItem map = new JMenuItem("Karte...");
map.setMnemonic('k');
map.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MainController.getInstance().manageMaps();
}
});
JCheckBoxMenuItem osm = new JCheckBoxMenuItem("OSM-Renderer verwenden");
osm.setMnemonic('o');
osm.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
AbstractButton aButton = (AbstractButton) event.getSource();
boolean selected = aButton.getModel().isSelected();
if (selected) {
MainController.getInstance().setUseOnlineMaps(true);
} else {
MainController.getInstance().setUseOnlineMaps(false);
}
updateRenderer();
}
});
JCheckBoxMenuItem useArc = new JCheckBoxMenuItem("ArcFlags verwenden",
true);
useArc.setMnemonic('a');
useArc.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
AbstractButton aButton = (AbstractButton) event.getSource();
boolean selected = aButton.getModel().isSelected();
if (selected) {
MainController.getInstance().setUseArcFlags(true);
} else {
MainController.getInstance().setUseArcFlags(false);
}
}
});
admin.add(profile);
admin.add(map);
admin.add(osm);
admin.add(useArc);
menu.add(routeKIT);
menu.add(export);
menu.add(admin);
routeKIT.setMnemonic('r');
export.setMnemonic('e');
admin.setMnemonic('v');
setJMenuBar(menu);
}
/**
* Updates the display of the current profile.
*
* @param profile
* The new profile.
*/
public void setCurrentProfile(Profile profile) {
profileLabel.setText(profile.getName());
}
/**
* Updates the display of the current map.
*
* @param map
* The new map.
*/
public void setCurrentMap(StreetMap map) {
mapLabel.setText(map.getName());
}
@Override
public void routeModelChanged() {
Coordinates start = routeModel.getStart();
Coordinates destination = routeModel.getDestination();
final RouteDescription description = routeModel.getCurrentDescription();
if (routeModel.getCurrentRoute() == null) {
html.setEnabled(false);
gpx.setEnabled(false);
} else {
html.setEnabled(true);
gpx.setEnabled(true);
}
if (start != null) {
startField.setBackground(Color.WHITE);
startField
.setText(start.getLatitude() + " " + start.getLongitude());
}
if (destination != null) {
targetField.setBackground(Color.WHITE);
targetField.setText(destination.getLatitude() + " "
+ destination.getLongitude());
}
if (start != null && destination != null) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
DefaultListModel<String> listModel = (DefaultListModel<String>) routeDescription
.getModel();
listModel.clear();
if (routeModel.isCalculating()) {
listModel.addElement("Route wird berechnet...");
return;
}
if (routeModel.getCurrentRoute() == null) {
listModel.addElement("Keine Route gefunden.");
return;
}
if (description == null) {
listModel.addElement("Beschreibung wird erstellt...");
return;
}
for (TurnInstruction instruction : description
.getInstructions()) {
listModel.addElement(instruction.toString());
}
listModel.addElement("Sie haben Ihr Ziel erreicht.");
}
});
}
}
public void textMessage(String str) {
int i = 0;
int iPrev = 0;
int width = routeScroll.getParent().getPreferredSize().width / 7;
DefaultListModel<String> listModel = (DefaultListModel<String>) routeDescription
.getModel();
listModel.clear();
while (i + width < str.length()
&& (i = str.lastIndexOf(" ", i + width)) != -1) {
listModel.addElement(str.substring(iPrev, i));
iPrev = i + 1; // + 1 for skipping the space
}
listModel.addElement(str.substring(iPrev));
}
}